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.bin.Cache;
26 import jalview.datamodel.AlignmentI;
27 import jalview.datamodel.SequenceI;
28 import jalview.datamodel.features.FeatureAttributes;
29 import jalview.gui.Help.HelpId;
30 import jalview.io.JalviewFileChooser;
31 import jalview.io.JalviewFileView;
32 import jalview.schemabinding.version2.JalviewUserColours;
33 import jalview.schemes.FeatureColour;
34 import jalview.util.Format;
35 import jalview.util.MessageManager;
36 import jalview.util.Platform;
37 import jalview.util.QuickSort;
38 import jalview.util.ReverseListIterator;
39 import jalview.util.matcher.Condition;
40 import jalview.util.matcher.KeyedMatcher;
41 import jalview.util.matcher.KeyedMatcherI;
42 import jalview.util.matcher.KeyedMatcherSet;
43 import jalview.util.matcher.KeyedMatcherSetI;
44 import jalview.viewmodel.AlignmentViewport;
45 import jalview.ws.DasSequenceFeatureFetcher;
46 import jalview.ws.dbsources.das.api.jalviewSourceI;
48 import java.awt.BorderLayout;
49 import java.awt.Color;
50 import java.awt.Component;
51 import java.awt.Dimension;
52 import java.awt.FlowLayout;
54 import java.awt.Graphics;
55 import java.awt.GridLayout;
56 import java.awt.Rectangle;
57 import java.awt.event.ActionEvent;
58 import java.awt.event.ActionListener;
59 import java.awt.event.FocusAdapter;
60 import java.awt.event.FocusEvent;
61 import java.awt.event.ItemEvent;
62 import java.awt.event.ItemListener;
63 import java.awt.event.MouseAdapter;
64 import java.awt.event.MouseEvent;
65 import java.awt.event.MouseMotionAdapter;
66 import java.beans.PropertyChangeEvent;
67 import java.beans.PropertyChangeListener;
69 import java.io.FileInputStream;
70 import java.io.FileOutputStream;
71 import java.io.InputStreamReader;
72 import java.io.OutputStreamWriter;
73 import java.io.PrintWriter;
74 import java.util.ArrayList;
75 import java.util.Arrays;
76 import java.util.HashSet;
77 import java.util.Hashtable;
78 import java.util.Iterator;
79 import java.util.List;
82 import java.util.Vector;
84 import javax.help.HelpSetException;
85 import javax.swing.AbstractCellEditor;
86 import javax.swing.BorderFactory;
87 import javax.swing.ButtonGroup;
88 import javax.swing.Icon;
89 import javax.swing.JButton;
90 import javax.swing.JCheckBox;
91 import javax.swing.JCheckBoxMenuItem;
92 import javax.swing.JColorChooser;
93 import javax.swing.JComboBox;
94 import javax.swing.JDialog;
95 import javax.swing.JInternalFrame;
96 import javax.swing.JLabel;
97 import javax.swing.JLayeredPane;
98 import javax.swing.JMenuItem;
99 import javax.swing.JPanel;
100 import javax.swing.JPopupMenu;
101 import javax.swing.JRadioButton;
102 import javax.swing.JScrollPane;
103 import javax.swing.JSlider;
104 import javax.swing.JTabbedPane;
105 import javax.swing.JTable;
106 import javax.swing.JTextArea;
107 import javax.swing.JTextField;
108 import javax.swing.ListSelectionModel;
109 import javax.swing.SwingConstants;
110 import javax.swing.SwingUtilities;
111 import javax.swing.event.ChangeEvent;
112 import javax.swing.event.ChangeListener;
113 import javax.swing.plaf.basic.BasicArrowButton;
114 import javax.swing.table.AbstractTableModel;
115 import javax.swing.table.TableCellEditor;
116 import javax.swing.table.TableCellRenderer;
118 public class FeatureSettings extends JPanel
119 implements FeatureSettingsControllerI
121 private static final int MIN_WIDTH = 400;
123 private static final int MIN_HEIGHT = 400;
125 DasSourceBrowser dassourceBrowser;
127 DasSequenceFeatureFetcher dasFeatureFetcher;
129 JPanel dasSettingsPane = new JPanel();
131 final FeatureRenderer fr;
133 public final AlignFrame af;
136 * 'original' fields hold settings to restore on Cancel
138 Object[][] originalData;
140 private float originalTransparency;
142 private Map<String, KeyedMatcherSetI> originalFilters;
144 final JInternalFrame frame;
146 JScrollPane scrollPane = new JScrollPane();
152 JSlider transparency = new JSlider();
155 * when true, constructor is still executing - so ignore UI events
157 protected volatile boolean inConstruction = true;
159 int selectedRow = -1;
161 JButton fetchDAS = new JButton();
163 JButton saveDAS = new JButton();
165 JButton cancelDAS = new JButton();
167 boolean resettingTable = false;
170 * true when Feature Settings are updating from feature renderer
172 private boolean handlingUpdate = false;
175 * holds {featureCount, totalExtent} for each feature type
177 Map<String, float[]> typeWidth = null;
180 * fields of the feature filters tab
182 private JPanel filtersPane;
184 private JPanel chooseFiltersPanel;
186 private JComboBox<String> filteredFeatureChoice;
188 private JRadioButton andFilters;
190 private JRadioButton orFilters;
193 * filters for the currently selected feature type
195 private List<KeyedMatcherI> filters;
197 private JTextArea filtersAsText;
204 public FeatureSettings(AlignFrame alignFrame)
206 this.af = alignFrame;
207 fr = af.getFeatureRenderer();
209 // save transparency for restore on Cancel
210 originalTransparency = fr.getTransparency();
211 int originalTransparencyAsPercent = (int) (originalTransparency * 100);
212 transparency.setMaximum(100 - originalTransparencyAsPercent);
214 originalFilters = fr.getFeatureFilters();
219 } catch (Exception ex)
221 ex.printStackTrace();
227 public String getToolTipText(MouseEvent e)
229 if (table.columnAtPoint(e.getPoint()) == 0)
232 * Tooltip for feature name only
234 return JvSwingUtils.wrapTooltip(true, MessageManager
235 .getString("label.feature_settings_click_drag"));
240 table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
241 table.setFont(new Font("Verdana", Font.PLAIN, 12));
242 table.setDefaultRenderer(Color.class, new ColorRenderer());
244 table.setDefaultEditor(Color.class, new ColorEditor(this));
246 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
247 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
248 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
250 table.addMouseListener(new MouseAdapter()
253 public void mousePressed(MouseEvent evt)
255 selectedRow = table.rowAtPoint(evt.getPoint());
256 if (evt.isPopupTrigger())
258 popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
259 table.getValueAt(selectedRow, 1), fr.getMinMax(),
260 evt.getX(), evt.getY());
262 else if (evt.getClickCount() == 2)
264 boolean invertSelection = evt.isAltDown();
265 boolean toggleSelection = Platform.isControlDown(evt);
266 boolean extendSelection = evt.isShiftDown();
267 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
268 invertSelection, extendSelection, toggleSelection,
269 (String) table.getValueAt(selectedRow, 0));
273 // isPopupTrigger fires on mouseReleased on Windows
275 public void mouseReleased(MouseEvent evt)
277 selectedRow = table.rowAtPoint(evt.getPoint());
278 if (evt.isPopupTrigger())
280 popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
281 table.getValueAt(selectedRow, 1), fr.getMinMax(),
282 evt.getX(), evt.getY());
287 table.addMouseMotionListener(new MouseMotionAdapter()
290 public void mouseDragged(MouseEvent evt)
292 int newRow = table.rowAtPoint(evt.getPoint());
293 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
296 * reposition 'selectedRow' to 'newRow' (the dragged to location)
297 * this could be more than one row away for a very fast drag action
298 * so just swap it with adjacent rows until we get it there
300 Object[][] data = ((FeatureTableModel) table.getModel())
302 int direction = newRow < selectedRow ? -1 : 1;
303 for (int i = selectedRow; i != newRow; i += direction)
305 Object[] temp = data[i];
306 data[i] = data[i + direction];
307 data[i + direction] = temp;
309 updateFeatureRenderer(data);
311 selectedRow = newRow;
315 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
316 // MessageManager.getString("label.feature_settings_click_drag")));
317 scrollPane.setViewportView(table);
319 dassourceBrowser = new DasSourceBrowser(this);
320 dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
322 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
324 fr.findAllFeatures(true); // display everything!
327 discoverAllFeatureData();
328 final PropertyChangeListener change;
329 final FeatureSettings fs = this;
330 fr.addPropertyChangeListener(change = new PropertyChangeListener()
333 public void propertyChange(PropertyChangeEvent evt)
335 if (!fs.resettingTable && !fs.handlingUpdate)
337 fs.handlingUpdate = true;
338 fs.resetTable(null); // new groups may be added with new seuqence
339 // feature types only
340 fs.handlingUpdate = false;
346 frame = new JInternalFrame();
347 frame.setContentPane(this);
348 if (Platform.isAMac())
350 Desktop.addInternalFrame(frame,
351 MessageManager.getString("label.sequence_feature_settings"),
356 Desktop.addInternalFrame(frame,
357 MessageManager.getString("label.sequence_feature_settings"),
360 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
362 frame.addInternalFrameListener(
363 new javax.swing.event.InternalFrameAdapter()
366 public void internalFrameClosed(
367 javax.swing.event.InternalFrameEvent evt)
369 fr.removePropertyChangeListener(change);
370 dassourceBrowser.fs = null;
373 frame.setLayer(JLayeredPane.PALETTE_LAYER);
374 inConstruction = false;
377 protected void popupSort(final int selectedRow, final String type,
378 final Object typeCol, final Map<String, float[][]> minmax, int x,
381 final FeatureColourI featureColour = (FeatureColourI) typeCol;
383 JPopupMenu men = new JPopupMenu(MessageManager
384 .formatMessage("label.settings_for_param", new String[]
386 JMenuItem scr = new JMenuItem(
387 MessageManager.getString("label.sort_by_score"));
389 final FeatureSettings me = this;
390 scr.addActionListener(new ActionListener()
394 public void actionPerformed(ActionEvent e)
397 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
402 JMenuItem dens = new JMenuItem(
403 MessageManager.getString("label.sort_by_density"));
404 dens.addActionListener(new ActionListener()
408 public void actionPerformed(ActionEvent e)
411 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
419 final float[][] typeMinMax = minmax.get(type);
421 * final JCheckBoxMenuItem chb = new JCheckBoxMenuItem("Vary Height"); //
422 * this is broken at the moment and isn't that useful anyway!
423 * chb.setSelected(minmax.get(type) != null); chb.addActionListener(new
426 * public void actionPerformed(ActionEvent e) {
427 * chb.setState(chb.getState()); if (chb.getState()) { minmax.put(type,
428 * null); } else { minmax.put(type, typeMinMax); } }
434 if (typeMinMax != null && typeMinMax[0] != null)
436 // if (table.getValueAt(row, column));
437 // graduated colourschemes for those where minmax exists for the
438 // positional features
439 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
441 mxcol.setSelected(!featureColour.isSimpleColour());
443 mxcol.addActionListener(new ActionListener()
445 JColorChooser colorChooser;
448 public void actionPerformed(ActionEvent e)
450 if (e.getSource() == mxcol)
452 if (featureColour.isSimpleColour())
454 FeatureColourChooser fc = new FeatureColourChooser(me.fr,
456 fc.addActionListener(this);
460 // bring up simple color chooser
461 colorChooser = new JColorChooser();
462 JDialog dialog = JColorChooser.createDialog(me,
463 "Select new Colour", true, // modal
464 colorChooser, this, // OK button handler
465 null); // no CANCEL button handler
466 colorChooser.setColor(featureColour.getMaxColour());
467 dialog.setVisible(true);
472 if (e.getSource() instanceof FeatureColourChooser)
474 FeatureColourChooser fc = (FeatureColourChooser) e
476 table.setValueAt(fc.getLastColour(), selectedRow, 1);
481 // probably the color chooser!
482 table.setValueAt(new FeatureColour(colorChooser.getColor()),
485 me.updateFeatureRenderer(
486 ((FeatureTableModel) table.getModel()).getData(),
495 JMenuItem selCols = new JMenuItem(
496 MessageManager.getString("label.select_columns_containing"));
497 selCols.addActionListener(new ActionListener()
500 public void actionPerformed(ActionEvent arg0)
502 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
506 JMenuItem clearCols = new JMenuItem(MessageManager
507 .getString("label.select_columns_not_containing"));
508 clearCols.addActionListener(new ActionListener()
511 public void actionPerformed(ActionEvent arg0)
513 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
517 JMenuItem hideCols = new JMenuItem(
518 MessageManager.getString("label.hide_columns_containing"));
519 hideCols.addActionListener(new ActionListener()
522 public void actionPerformed(ActionEvent arg0)
524 fr.ap.alignFrame.hideFeatureColumns(type, true);
527 JMenuItem hideOtherCols = new JMenuItem(
528 MessageManager.getString("label.hide_columns_not_containing"));
529 hideOtherCols.addActionListener(new ActionListener()
532 public void actionPerformed(ActionEvent arg0)
534 fr.ap.alignFrame.hideFeatureColumns(type, false);
540 men.add(hideOtherCols);
541 men.show(table, x, y);
545 synchronized public void discoverAllFeatureData()
547 Set<String> allGroups = new HashSet<>();
548 AlignmentI alignment = af.getViewport().getAlignment();
550 for (int i = 0; i < alignment.getHeight(); i++)
552 SequenceI seq = alignment.getSequenceAt(i);
553 for (String group : seq.getFeatures().getFeatureGroups(true))
555 if (group != null && !allGroups.contains(group))
557 allGroups.add(group);
558 checkGroupState(group);
563 populateFilterableFeatures();
571 * Synchronise gui group list and check visibility of group
574 * @return true if group is visible
576 private boolean checkGroupState(String group)
578 boolean visible = fr.checkGroupVisibility(group, true);
580 for (int g = 0; g < groupPanel.getComponentCount(); g++)
582 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
584 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
589 final String grp = group;
590 final JCheckBox check = new JCheckBox(group, visible);
591 check.setFont(new Font("Serif", Font.BOLD, 12));
592 check.setToolTipText(group);
593 check.addItemListener(new ItemListener()
596 public void itemStateChanged(ItemEvent evt)
598 fr.setGroupVisibility(check.getText(), check.isSelected());
599 resetTable(new String[] { grp });
600 af.alignPanel.paintAlignment(true, true);
603 groupPanel.add(check);
607 synchronized void resetTable(String[] groupChanged)
613 resettingTable = true;
614 typeWidth = new Hashtable<>();
615 // TODO: change avWidth calculation to 'per-sequence' average and use long
618 Set<String> displayableTypes = new HashSet<>();
619 Set<String> foundGroups = new HashSet<>();
622 * determine which feature types may be visible depending on
623 * which groups are selected, and recompute average width data
625 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
628 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
631 * get the sequence's groups for positional features
632 * and keep track of which groups are visible
634 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
635 Set<String> visibleGroups = new HashSet<>();
636 for (String group : groups)
638 if (group == null || checkGroupState(group))
640 visibleGroups.add(group);
643 foundGroups.addAll(groups);
646 * get distinct feature types for visible groups
647 * record distinct visible types, and their count and total length
649 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
650 visibleGroups.toArray(new String[visibleGroups.size()]));
651 for (String type : types)
653 displayableTypes.add(type);
654 float[] avWidth = typeWidth.get(type);
657 avWidth = new float[2];
658 typeWidth.put(type, avWidth);
660 // todo this could include features with a non-visible group
661 // - do we greatly care?
662 // todo should we include non-displayable features here, and only
663 // update when features are added?
664 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
665 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
669 Object[][] data = new Object[displayableTypes.size()][3];
672 if (fr.hasRenderOrder())
676 fr.findAllFeatures(groupChanged != null); // prod to update
677 // colourschemes. but don't
679 // First add the checks in the previous render order,
680 // in case the window has been closed and reopened
682 List<String> frl = fr.getRenderOrder();
683 for (int ro = frl.size() - 1; ro > -1; ro--)
685 String type = frl.get(ro);
687 if (!displayableTypes.contains(type))
692 data[dataIndex][0] = type;
693 data[dataIndex][1] = fr.getFeatureStyle(type);
694 data[dataIndex][2] = new Boolean(
695 af.getViewport().getFeaturesDisplayed().isVisible(type));
697 displayableTypes.remove(type);
702 * process any extra features belonging only to
703 * a group which was just selected
705 while (!displayableTypes.isEmpty())
707 String type = displayableTypes.iterator().next();
708 data[dataIndex][0] = type;
710 data[dataIndex][1] = fr.getFeatureStyle(type);
711 if (data[dataIndex][1] == null)
713 // "Colour has been updated in another view!!"
714 fr.clearRenderOrder();
718 data[dataIndex][2] = new Boolean(true);
720 displayableTypes.remove(type);
723 if (originalData == null)
725 originalData = new Object[data.length][3];
726 for (int i = 0; i < data.length; i++)
728 System.arraycopy(data[i], 0, originalData[i], 0, 3);
733 updateOriginalData(data);
736 table.setModel(new FeatureTableModel(data));
737 table.getColumnModel().getColumn(0).setPreferredWidth(200);
739 groupPanel.setLayout(
740 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
741 pruneGroups(foundGroups);
742 groupPanel.validate();
744 updateFeatureRenderer(data, groupChanged != null);
745 resettingTable = false;
749 * Updates 'originalData' (used for restore on Cancel) if we detect that
750 * changes have been made outwith this dialog
752 * <li>a new feature type added (and made visible)</li>
753 * <li>a feature colour changed (in the Amend Features dialog)</li>
758 protected void updateOriginalData(Object[][] foundData)
760 // todo LinkedHashMap instead of Object[][] would be nice
762 Object[][] currentData = ((FeatureTableModel) table.getModel())
764 for (Object[] row : foundData)
766 String type = (String) row[0];
767 boolean found = false;
768 for (Object[] current : currentData)
770 if (type.equals(current[0]))
774 * currently dependent on object equality here;
775 * really need an equals method on FeatureColour
777 if (!row[1].equals(current[1]))
780 * feature colour has changed externally - update originalData
782 for (Object[] original : originalData)
784 if (type.equals(original[0]))
786 original[1] = row[1];
797 * new feature detected - add to original data (on top)
799 Object[][] newData = new Object[originalData.length + 1][3];
800 for (int i = 0; i < originalData.length; i++)
802 System.arraycopy(originalData[i], 0, newData[i + 1], 0, 3);
805 originalData = newData;
811 * Remove from the groups panel any checkboxes for groups that are not in the
812 * foundGroups set. This enables removing a group from the display when the
813 * last feature in that group is deleted.
817 protected void pruneGroups(Set<String> foundGroups)
819 for (int g = 0; g < groupPanel.getComponentCount(); g++)
821 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
822 if (!foundGroups.contains(checkbox.getText()))
824 groupPanel.remove(checkbox);
830 * reorder data based on the featureRenderers global priority list.
834 private void ensureOrder(Object[][] data)
836 boolean sort = false;
837 float[] order = new float[data.length];
838 for (int i = 0; i < order.length; i++)
840 order[i] = fr.getOrder(data[i][0].toString());
843 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
847 sort = sort || order[i - 1] > order[i];
852 jalview.util.QuickSort.sort(order, data);
858 JalviewFileChooser chooser = new JalviewFileChooser("fc",
859 "Sequence Feature Colours");
860 chooser.setFileView(new JalviewFileView());
861 chooser.setDialogTitle(
862 MessageManager.getString("label.load_feature_colours"));
863 chooser.setToolTipText(MessageManager.getString("action.load"));
865 int value = chooser.showOpenDialog(this);
867 if (value == JalviewFileChooser.APPROVE_OPTION)
869 File file = chooser.getSelectedFile();
873 InputStreamReader in = new InputStreamReader(
874 new FileInputStream(file), "UTF-8");
876 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
878 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
881 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
884 Color mincol = null, maxcol = null;
887 mincol = new Color(Integer.parseInt(newcol.getMinRGB(), 16));
888 maxcol = new Color(Integer.parseInt(newcol.getRGB(), 16));
890 } catch (Exception e)
892 Cache.log.warn("Couldn't parse out graduated feature color.",
895 FeatureColourI gcol = new FeatureColour(mincol, maxcol,
896 newcol.getMin(), newcol.getMax());
897 if (newcol.hasAutoScale())
899 gcol.setAutoScaled(newcol.getAutoScale());
901 if (newcol.hasColourByLabel())
903 gcol.setColourByLabel(newcol.getColourByLabel());
905 if (newcol.hasThreshold())
907 gcol.setThreshold(newcol.getThreshold());
909 if (newcol.getThreshType().length() > 0)
911 String ttyp = newcol.getThreshType();
912 if (ttyp.equalsIgnoreCase("ABOVE"))
914 gcol.setAboveThreshold(true);
916 if (ttyp.equalsIgnoreCase("BELOW"))
918 gcol.setBelowThreshold(true);
921 fr.setColour(name = newcol.getName(), gcol);
925 Color color = new Color(
926 Integer.parseInt(jucs.getColour(i).getRGB(), 16));
927 fr.setColour(name = jucs.getColour(i).getName(),
928 new FeatureColour(color));
930 fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
935 Object[][] data = ((FeatureTableModel) table.getModel())
938 updateFeatureRenderer(data, false);
941 } catch (Exception ex)
943 System.out.println("Error loading User Colour File\n" + ex);
950 JalviewFileChooser chooser = new JalviewFileChooser("fc",
951 "Sequence Feature Colours");
952 chooser.setFileView(new JalviewFileView());
953 chooser.setDialogTitle(
954 MessageManager.getString("label.save_feature_colours"));
955 chooser.setToolTipText(MessageManager.getString("action.save"));
957 int value = chooser.showSaveDialog(this);
959 if (value == JalviewFileChooser.APPROVE_OPTION)
961 String choice = chooser.getSelectedFile().getPath();
962 jalview.schemabinding.version2.JalviewUserColours ucs = new jalview.schemabinding.version2.JalviewUserColours();
963 ucs.setSchemeName("Sequence Features");
966 PrintWriter out = new PrintWriter(new OutputStreamWriter(
967 new FileOutputStream(choice), "UTF-8"));
969 Set<String> fr_colours = fr.getAllFeatureColours();
970 Iterator<String> e = fr_colours.iterator();
971 float[] sortOrder = new float[fr_colours.size()];
972 String[] sortTypes = new String[fr_colours.size()];
976 sortTypes[i] = e.next();
977 sortOrder[i] = fr.getOrder(sortTypes[i]);
980 QuickSort.sort(sortOrder, sortTypes);
982 for (i = 0; i < sortTypes.length; i++)
984 jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
985 col.setName(sortTypes[i]);
986 FeatureColourI fcol = fr.getFeatureStyle(sortTypes[i]);
987 if (fcol.isSimpleColour())
989 col.setRGB(Format.getHexString(fcol.getColour()));
993 col.setRGB(Format.getHexString(fcol.getMaxColour()));
994 col.setMin(fcol.getMin());
995 col.setMax(fcol.getMax());
997 jalview.util.Format.getHexString(fcol.getMinColour()));
998 col.setAutoScale(fcol.isAutoScaled());
999 col.setThreshold(fcol.getThreshold());
1000 col.setColourByLabel(fcol.isColourByLabel());
1001 col.setThreshType(fcol.isAboveThreshold() ? "ABOVE"
1002 : (fcol.isBelowThreshold() ? "BELOW" : "NONE"));
1008 } catch (Exception ex)
1010 ex.printStackTrace();
1015 public void invertSelection()
1017 for (int i = 0; i < table.getRowCount(); i++)
1019 Boolean value = (Boolean) table.getValueAt(i, 2);
1021 table.setValueAt(new Boolean(!value.booleanValue()), i, 2);
1025 public void orderByAvWidth()
1027 if (table == null || table.getModel() == null)
1031 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1032 float[] width = new float[data.length];
1036 for (int i = 0; i < data.length; i++)
1038 awidth = typeWidth.get(data[i][0]);
1041 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1042 // weight - but have to make per
1043 // sequence, too (awidth[2])
1044 // if (width[i]==1) // hack to distinguish single width sequences.
1056 boolean sort = false;
1057 for (int i = 0; i < width.length; i++)
1059 // awidth = (float[]) typeWidth.get(data[i][0]);
1062 width[i] = fr.getOrder(data[i][0].toString());
1065 width[i] = fr.setOrder(data[i][0].toString(), i / data.length);
1070 width[i] /= max; // normalize
1071 fr.setOrder(data[i][0].toString(), width[i]); // store for later
1075 sort = sort || width[i - 1] > width[i];
1080 jalview.util.QuickSort.sort(width, data);
1081 // update global priority order
1084 updateFeatureRenderer(data, false);
1092 frame.setClosed(true);
1093 } catch (Exception exe)
1099 public void updateFeatureRenderer(Object[][] data)
1101 updateFeatureRenderer(data, true);
1105 * Update the priority order of features; only repaint if this changed the
1106 * order of visible features
1111 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1113 if (fr.setFeaturePriority(data, visibleNew))
1115 af.alignPanel.paintAlignment(true, true);
1119 private void jbInit() throws Exception
1121 this.setLayout(new BorderLayout());
1123 JPanel settingsPane = new JPanel();
1124 settingsPane.setLayout(new BorderLayout());
1126 filtersPane = new JPanel();
1128 dasSettingsPane.setLayout(new BorderLayout());
1130 JPanel bigPanel = new JPanel();
1131 bigPanel.setLayout(new BorderLayout());
1133 groupPanel = new JPanel();
1134 bigPanel.add(groupPanel, BorderLayout.NORTH);
1136 JButton invert = new JButton(
1137 MessageManager.getString("label.invert_selection"));
1138 invert.setFont(JvSwingUtils.getLabelFont());
1139 invert.addActionListener(new ActionListener()
1142 public void actionPerformed(ActionEvent e)
1148 JButton optimizeOrder = new JButton(
1149 MessageManager.getString("label.optimise_order"));
1150 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1151 optimizeOrder.addActionListener(new ActionListener()
1154 public void actionPerformed(ActionEvent e)
1160 JButton sortByScore = new JButton(
1161 MessageManager.getString("label.seq_sort_by_score"));
1162 sortByScore.setFont(JvSwingUtils.getLabelFont());
1163 sortByScore.addActionListener(new ActionListener()
1166 public void actionPerformed(ActionEvent e)
1168 af.avc.sortAlignmentByFeatureScore(null);
1171 JButton sortByDens = new JButton(
1172 MessageManager.getString("label.sequence_sort_by_density"));
1173 sortByDens.setFont(JvSwingUtils.getLabelFont());
1174 sortByDens.addActionListener(new ActionListener()
1177 public void actionPerformed(ActionEvent e)
1179 af.avc.sortAlignmentByFeatureDensity(null);
1183 JButton help = new JButton(MessageManager.getString("action.help"));
1184 help.setFont(JvSwingUtils.getLabelFont());
1185 help.addActionListener(new ActionListener()
1188 public void actionPerformed(ActionEvent e)
1192 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1193 } catch (HelpSetException e1)
1195 e1.printStackTrace();
1199 help.setFont(JvSwingUtils.getLabelFont());
1200 help.setText(MessageManager.getString("action.help"));
1201 help.addActionListener(new ActionListener()
1204 public void actionPerformed(ActionEvent e)
1208 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1209 } catch (HelpSetException e1)
1211 e1.printStackTrace();
1216 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
1217 cancel.setFont(JvSwingUtils.getLabelFont());
1218 cancel.addActionListener(new ActionListener()
1221 public void actionPerformed(ActionEvent e)
1223 fr.setTransparency(originalTransparency);
1224 fr.setFeatureFilters(originalFilters);
1225 updateFeatureRenderer(originalData);
1230 JButton ok = new JButton(MessageManager.getString("action.ok"));
1231 ok.setFont(JvSwingUtils.getLabelFont());
1232 ok.addActionListener(new ActionListener()
1235 public void actionPerformed(ActionEvent e)
1241 JButton loadColours = new JButton(
1242 MessageManager.getString("label.load_colours"));
1243 loadColours.setFont(JvSwingUtils.getLabelFont());
1244 loadColours.addActionListener(new ActionListener()
1247 public void actionPerformed(ActionEvent e)
1253 JButton saveColours = new JButton(
1254 MessageManager.getString("label.save_colours"));
1255 saveColours.setFont(JvSwingUtils.getLabelFont());
1256 saveColours.addActionListener(new ActionListener()
1259 public void actionPerformed(ActionEvent e)
1264 transparency.addChangeListener(new ChangeListener()
1267 public void stateChanged(ChangeEvent evt)
1269 if (!inConstruction)
1271 fr.setTransparency((100 - transparency.getValue()) / 100f);
1272 af.alignPanel.paintAlignment(true,true);
1277 transparency.setMaximum(70);
1278 transparency.setToolTipText(
1279 MessageManager.getString("label.transparency_tip"));
1280 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1281 fetchDAS.addActionListener(new ActionListener()
1284 public void actionPerformed(ActionEvent e)
1286 fetchDAS_actionPerformed(e);
1289 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1290 saveDAS.addActionListener(new ActionListener()
1293 public void actionPerformed(ActionEvent e)
1295 saveDAS_actionPerformed(e);
1299 JPanel dasButtonPanel = new JPanel();
1300 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1301 dasSettingsPane.setBorder(null);
1302 cancelDAS.setEnabled(false);
1303 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1304 cancelDAS.addActionListener(new ActionListener()
1307 public void actionPerformed(ActionEvent e)
1309 cancelDAS_actionPerformed(e);
1313 JTabbedPane tabbedPane = new JTabbedPane();
1314 this.add(tabbedPane, BorderLayout.CENTER);
1315 tabbedPane.addTab(MessageManager.getString("label.feature_settings"),
1317 tabbedPane.addTab(MessageManager.getString("label.filters"),
1319 // tabbedPane.addTab(MessageManager.getString("label.das_settings"),
1320 // dasSettingsPane);
1322 JPanel transPanel = new JPanel(new GridLayout(1, 2));
1323 bigPanel.add(transPanel, BorderLayout.SOUTH);
1325 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1326 transbuttons.add(optimizeOrder);
1327 transbuttons.add(invert);
1328 transbuttons.add(sortByScore);
1329 transbuttons.add(sortByDens);
1330 transbuttons.add(help);
1331 transPanel.add(transparency);
1332 transPanel.add(transbuttons);
1334 JPanel buttonPanel = new JPanel();
1335 buttonPanel.add(ok);
1336 buttonPanel.add(cancel);
1337 buttonPanel.add(loadColours);
1338 buttonPanel.add(saveColours);
1339 bigPanel.add(scrollPane, BorderLayout.CENTER);
1340 dasSettingsPane.add(dasButtonPanel, BorderLayout.SOUTH);
1341 dasButtonPanel.add(fetchDAS);
1342 dasButtonPanel.add(cancelDAS);
1343 dasButtonPanel.add(saveDAS);
1344 settingsPane.add(bigPanel, BorderLayout.CENTER);
1345 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1351 * Populates initial layout of the feature attribute filters panel
1353 protected void initFiltersTab()
1355 filters = new ArrayList<>();
1358 * choose feature type
1360 JPanel chooseTypePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
1361 chooseTypePanel.setBackground(Color.white);
1362 chooseTypePanel.setBorder(BorderFactory
1363 .createTitledBorder(MessageManager
1364 .getString("label.feature_type")));
1365 filteredFeatureChoice = new JComboBox<>();
1366 filteredFeatureChoice.addItemListener(new ItemListener()
1369 public void itemStateChanged(ItemEvent e)
1371 refreshFiltersDisplay();
1374 chooseTypePanel.add(new JLabel(MessageManager
1375 .getString("label.feature_to_filter")));
1376 chooseTypePanel.add(filteredFeatureChoice);
1377 populateFilterableFeatures();
1380 * the panel with the filters for the selected feature type
1382 JPanel filtersPanel = new JPanel(new GridLayout(0, 1));
1383 filtersPanel.setBackground(Color.white);
1384 filtersPanel.setBorder(BorderFactory
1385 .createTitledBorder(MessageManager.getString("label.filters")));
1388 * add AND or OR radio buttons
1390 JPanel andOrPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
1391 andOrPanel.setBackground(Color.white);
1392 andFilters = new JRadioButton("And");
1393 orFilters = new JRadioButton("Or");
1394 ActionListener actionListener = new ActionListener()
1397 public void actionPerformed(ActionEvent e)
1402 andFilters.addActionListener(actionListener);
1403 orFilters.addActionListener(actionListener);
1404 ButtonGroup andOr = new ButtonGroup();
1405 andOr.add(andFilters);
1406 andOr.add(orFilters);
1407 andFilters.setSelected(true);
1408 andOrPanel.add(new JLabel(MessageManager
1409 .getString("label.join_conditions")));
1410 andOrPanel.add(andFilters);
1411 andOrPanel.add(orFilters);
1412 filtersPanel.add(andOrPanel);
1415 * panel with filters - populated by refreshFiltersDisplay
1417 chooseFiltersPanel = new JPanel(new GridLayout(0, 1));
1418 filtersPanel.add(chooseFiltersPanel);
1421 * a read-only text view of the current filters
1423 JPanel showFiltersPanel = new JPanel(new BorderLayout(5, 5));
1424 showFiltersPanel.setBackground(Color.white);
1425 showFiltersPanel.setBorder(BorderFactory
1426 .createTitledBorder(MessageManager
1427 .getString("label.match_condition")));
1428 filtersAsText = new JTextArea();
1429 filtersAsText.setLineWrap(true);
1430 filtersAsText.setWrapStyleWord(true);
1431 showFiltersPanel.add(filtersAsText);
1433 filtersPane.setLayout(new BorderLayout());
1434 filtersPane.add(chooseTypePanel, BorderLayout.NORTH);
1435 filtersPane.add(filtersPanel, BorderLayout.CENTER);
1436 filtersPane.add(showFiltersPanel, BorderLayout.SOUTH);
1439 * update display for initial feature type selection
1441 refreshFiltersDisplay();
1445 * Adds entries to the 'choose feature to filter' drop-down choice. Only
1446 * feature types which have known attributes (so can be filtered) are
1447 * included, so recall this method to update the list (check for newly added
1450 protected void populateFilterableFeatures()
1453 * suppress action handler while updating the list
1455 ItemListener listener = filteredFeatureChoice.getItemListeners()[0];
1456 filteredFeatureChoice.removeItemListener(listener);
1458 filteredFeatureChoice.removeAllItems();
1459 ReverseListIterator<String> types = new ReverseListIterator<>(
1460 fr.getRenderOrder());
1462 boolean found = false;
1463 while (types.hasNext())
1465 String type = types.next();
1466 if (FeatureAttributes.getInstance().hasAttributes(type))
1468 filteredFeatureChoice.addItem(type);
1474 filteredFeatureChoice
1475 .addItem("No filterable feature attributes known");
1478 filteredFeatureChoice.addItemListener(listener);
1483 * Refreshes the display to show any filters currently configured for the
1484 * selected feature type (editable, with 'remove' option), plus one extra row
1485 * for adding a condition. This should be called on change of selected feature
1486 * type, or after a filter has been removed, added or amended.
1488 protected void refreshFiltersDisplay()
1491 * clear the panel and list of filter conditions
1493 chooseFiltersPanel.removeAll();
1495 String selectedType = (String) filteredFeatureChoice.getSelectedItem();
1500 * look up attributes known for feature type
1502 List<String> attNames = FeatureAttributes.getInstance().getAttributes(
1506 * if this feature type has filters set, load them first
1508 KeyedMatcherSetI featureFilters = fr.getFeatureFilter(selectedType);
1509 filtersAsText.setText("");
1510 if (featureFilters != null)
1512 filtersAsText.setText(featureFilters.toString());
1513 if (!featureFilters.isAnded())
1515 orFilters.setSelected(true);
1517 Iterator<KeyedMatcherI> matchers = featureFilters.getMatchers();
1518 while (matchers.hasNext())
1520 filters.add(matchers.next());
1525 * and an empty filter for the user to populate (add)
1527 KeyedMatcherI noFilter = new KeyedMatcher("", Condition.values()[0], "");
1528 filters.add(noFilter);
1531 * render the conditions in rows, each in its own JPanel
1534 for (KeyedMatcherI filter : filters)
1536 String key = filter.getKey();
1537 Condition condition = filter.getMatcher()
1539 String pattern = filter.getMatcher().getPattern();
1540 JPanel row = addFilter(key, attNames, condition, pattern, i);
1541 chooseFiltersPanel.add(row);
1545 filtersPane.validate();
1546 filtersPane.repaint();
1550 * A helper method that constructs a panel with one filter condition:
1552 * <li>a drop-down list of attribute names to choose from</li>
1553 * <li>a drop-down list of conditions to choose from</li>
1554 * <li>a text field for input of a match pattern</li>
1555 * <li>optionally, a 'remove' button</li>
1557 * If attribute, condition or pattern are not null, they are set as defaults
1558 * for the input fields. The 'remove' button is added unless the pattern is
1559 * null or empty (incomplete filter condition).
1568 protected JPanel addFilter(String attribute, List<String> attNames,
1569 Condition cond, String pattern, int i)
1571 JPanel filterRow = new JPanel(new FlowLayout(FlowLayout.LEFT));
1572 filterRow.setBackground(Color.white);
1575 * inputs for attribute, condition, pattern
1577 JComboBox<String> attCombo = new JComboBox<>();
1578 JComboBox<Condition> condCombo = new JComboBox<>();
1579 JTextField patternField = new JTextField(8);
1582 * action handlers that validate and (if valid) apply changes
1584 ActionListener actionListener = new ActionListener()
1587 public void actionPerformed(ActionEvent e)
1589 if (attCombo.getSelectedItem() != null)
1591 if (validateFilter(patternField, condCombo))
1593 updateFilter(attCombo, condCombo, patternField, i);
1599 ItemListener itemListener = new ItemListener()
1602 public void itemStateChanged(ItemEvent e)
1604 actionListener.actionPerformed(null);
1609 * drop-down choice of attribute
1611 if (attNames.isEmpty())
1613 attCombo.addItem("---");
1614 attCombo.setToolTipText(MessageManager
1615 .getString("label.no_attributes_known"));
1619 attCombo.setToolTipText("");
1620 for (String attName : attNames)
1622 attCombo.addItem(attName);
1624 if ("".equals(attribute))
1626 attCombo.setSelectedItem(null);
1630 attCombo.setSelectedItem(attribute);
1632 attCombo.addItemListener(itemListener);
1634 filterRow.add(attCombo);
1637 * drop-down choice of test condition
1639 for (Condition c : Condition.values())
1641 condCombo.addItem(c);
1645 condCombo.setSelectedItem(cond);
1647 condCombo.addItemListener(itemListener);
1648 filterRow.add(condCombo);
1651 * pattern to match against
1653 patternField.setText(pattern);
1654 patternField.addActionListener(actionListener);
1655 patternField.addFocusListener(new FocusAdapter()
1658 public void focusLost(FocusEvent e)
1660 actionListener.actionPerformed(null);
1663 filterRow.add(patternField);
1666 * add remove button if filter is populated (non-empty pattern)
1668 if (pattern != null && pattern.trim().length() > 0)
1670 // todo: gif for - button
1671 JButton removeCondition = new BasicArrowButton(SwingConstants.WEST);
1672 removeCondition.setToolTipText(MessageManager
1673 .getString("label.delete_row"));
1674 removeCondition.addActionListener(new ActionListener()
1677 public void actionPerformed(ActionEvent e)
1683 filterRow.add(removeCondition);
1690 * Action on any change to feature filtering, namely
1692 * <li>change of selected attribute</li>
1693 * <li>change of selected condition</li>
1694 * <li>change of match pattern</li>
1695 * <li>removal of a condition</li>
1697 * The action should be to
1699 * <li>parse and validate the filters</li>
1700 * <li>if valid, update the filter text box</li>
1701 * <li>and apply the filters to the viewport</li>
1704 protected void filtersChanged()
1707 * update the filter conditions for the feature type
1709 String featureType = (String) filteredFeatureChoice.getSelectedItem();
1710 boolean anded = andFilters.isSelected();
1711 KeyedMatcherSetI combined = new KeyedMatcherSet();
1713 for (KeyedMatcherI filter : filters)
1715 String pattern = filter.getMatcher().getPattern();
1716 if (pattern.trim().length() > 0)
1720 combined.and(filter);
1724 combined.or(filter);
1730 * save the filter conditions in the FeatureRenderer
1731 * (note this might now be an empty filter with no conditions)
1733 fr.setFeatureFilter(featureType, combined);
1735 filtersAsText.setText(combined.toString());
1737 refreshFiltersDisplay();
1739 af.alignPanel.paintAlignment(true, true);
1743 * Constructs a filter condition from the given input fields, and replaces the
1744 * condition at filterIndex with the new one
1749 * @param filterIndex
1751 protected void updateFilter(JComboBox<String> attCombo,
1752 JComboBox<Condition> condCombo, JTextField valueField,
1755 String attName = (String) attCombo.getSelectedItem();
1756 Condition cond = (Condition) condCombo.getSelectedItem();
1757 String pattern = valueField.getText();
1758 KeyedMatcherI km = new KeyedMatcher(attName, cond, pattern);
1760 filters.set(filterIndex, km);
1763 public void fetchDAS_actionPerformed(ActionEvent e)
1765 fetchDAS.setEnabled(false);
1766 cancelDAS.setEnabled(true);
1767 dassourceBrowser.setGuiEnabled(false);
1768 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1769 .getSelectedSources();
1770 doDasFeatureFetch(selectedSources, true, true);
1774 * get the features from selectedSources for all or the current selection
1776 * @param selectedSources
1777 * @param checkDbRefs
1778 * @param promptFetchDbRefs
1780 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1781 boolean checkDbRefs, boolean promptFetchDbRefs)
1783 SequenceI[] dataset, seqs;
1785 AlignmentViewport vp = af.getViewport();
1786 if (vp.getSelectionGroup() != null
1787 && vp.getSelectionGroup().getSize() > 0)
1789 iSize = vp.getSelectionGroup().getSize();
1790 dataset = new SequenceI[iSize];
1791 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1795 iSize = vp.getAlignment().getHeight();
1796 seqs = vp.getAlignment().getSequencesArray();
1799 dataset = new SequenceI[iSize];
1800 for (int i = 0; i < iSize; i++)
1802 dataset[i] = seqs[i].getDatasetSequence();
1805 cancelDAS.setEnabled(true);
1806 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1807 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1808 af.getViewport().setShowSequenceFeatures(true);
1809 af.showSeqFeatures.setSelected(true);
1813 * blocking call to initialise the das source browser
1815 public void initDasSources()
1817 dassourceBrowser.initDasSources();
1821 * examine the current list of das sources and return any matching the given
1822 * nicknames in sources
1825 * Vector of Strings to resolve to DAS source nicknames.
1826 * @return sources that are present in source list.
1828 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1830 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1834 * get currently selected das sources. ensure you have called initDasSources
1835 * before calling this.
1837 * @return vector of selected das source nicknames
1839 public Vector<jalviewSourceI> getSelectedSources()
1841 return dassourceBrowser.getSelectedSources();
1845 * properly initialise DAS fetcher and then initiate a new thread to fetch
1846 * features from the named sources (rather than any turned on by default)
1850 * if true then runs in same thread, otherwise passes to the Swing
1853 public void fetchDasFeatures(Vector<String> sources, boolean block)
1856 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1857 .resolveSourceNicknames(sources);
1858 if (resolved.size() == 0)
1860 resolved = dassourceBrowser.getSelectedSources();
1862 if (resolved.size() > 0)
1864 final List<jalviewSourceI> dassources = resolved;
1865 fetchDAS.setEnabled(false);
1866 // cancelDAS.setEnabled(true); doDasFetch does this.
1867 Runnable fetcher = new Runnable()
1873 doDasFeatureFetch(dassources, true, false);
1883 SwingUtilities.invokeLater(fetcher);
1888 public void saveDAS_actionPerformed(ActionEvent e)
1891 .saveProperties(jalview.bin.Cache.applicationProperties);
1894 public void complete()
1896 fetchDAS.setEnabled(true);
1897 cancelDAS.setEnabled(false);
1898 dassourceBrowser.setGuiEnabled(true);
1902 public void cancelDAS_actionPerformed(ActionEvent e)
1904 if (dasFeatureFetcher != null)
1906 dasFeatureFetcher.cancel();
1911 public void noDasSourceActive()
1914 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1915 MessageManager.getString("label.no_das_sources_selected_warn"),
1916 MessageManager.getString("label.no_das_sources_selected_title"),
1917 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1921 * Answers true unless a numeric condition has been selected with a
1922 * non-numeric value. Sets the value field to RED with a tooltip if in error.
1924 * If the pattern entered is empty, this method returns false, but does not
1925 * mark the field as invalid. This supports selecting an attribute for a new
1926 * condition before a match pattern has been entered.
1931 protected boolean validateFilter(JTextField value,
1932 JComboBox<Condition> condCombo)
1934 if (value == null || condCombo == null)
1936 return true; // fields not populated
1939 Condition cond = (Condition) condCombo.getSelectedItem();
1940 value.setBackground(Color.white);
1941 value.setToolTipText("");
1942 String v1 = value.getText().trim();
1943 if (v1.length() == 0)
1948 if (cond.isNumeric())
1953 } catch (NumberFormatException e)
1955 value.setBackground(Color.red);
1956 value.setToolTipText(MessageManager
1957 .getString("label.numeric_required"));
1965 // ///////////////////////////////////////////////////////////////////////
1966 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1967 // ///////////////////////////////////////////////////////////////////////
1968 class FeatureTableModel extends AbstractTableModel
1970 FeatureTableModel(Object[][] data)
1975 private String[] columnNames = {
1976 MessageManager.getString("label.feature_type"),
1977 MessageManager.getString("action.colour"),
1978 MessageManager.getString("label.display") };
1980 private Object[][] data;
1982 public Object[][] getData()
1987 public void setData(Object[][] data)
1993 public int getColumnCount()
1995 return columnNames.length;
1998 public Object[] getRow(int row)
2004 public int getRowCount()
2010 public String getColumnName(int col)
2012 return columnNames[col];
2016 public Object getValueAt(int row, int col)
2018 return data[row][col];
2022 public Class getColumnClass(int c)
2024 return getValueAt(0, c).getClass();
2028 public boolean isCellEditable(int row, int col)
2030 return col == 0 ? false : true;
2034 public void setValueAt(Object value, int row, int col)
2036 data[row][col] = value;
2037 fireTableCellUpdated(row, col);
2038 updateFeatureRenderer(data);
2043 class ColorRenderer extends JLabel implements TableCellRenderer
2045 javax.swing.border.Border unselectedBorder = null;
2047 javax.swing.border.Border selectedBorder = null;
2049 final String baseTT = "Click to edit, right/apple click for menu.";
2051 public ColorRenderer()
2053 setOpaque(true); // MUST do this for background to show up.
2054 setHorizontalTextPosition(SwingConstants.CENTER);
2055 setVerticalTextPosition(SwingConstants.CENTER);
2059 public Component getTableCellRendererComponent(JTable tbl, Object color,
2060 boolean isSelected, boolean hasFocus, int row, int column)
2062 FeatureColourI cellColour = (FeatureColourI) color;
2063 // JLabel comp = new JLabel();
2067 // setBounds(getBounds());
2069 setToolTipText(baseTT);
2070 setBackground(tbl.getBackground());
2071 if (!cellColour.isSimpleColour())
2073 Rectangle cr = tbl.getCellRect(row, column, false);
2074 FeatureSettings.renderGraduatedColor(this, cellColour,
2075 (int) cr.getWidth(), (int) cr.getHeight());
2082 newColor = cellColour.getColour();
2083 setBackground(newColor);
2087 if (selectedBorder == null)
2089 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
2090 tbl.getSelectionBackground());
2092 setBorder(selectedBorder);
2096 if (unselectedBorder == null)
2098 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
2099 tbl.getBackground());
2101 setBorder(unselectedBorder);
2109 * update comp using rendering settings from gcol
2114 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
2116 int w = comp.getWidth(), h = comp.getHeight();
2119 w = (int) comp.getPreferredSize().getWidth();
2120 h = (int) comp.getPreferredSize().getHeight();
2127 renderGraduatedColor(comp, gcol, w, h);
2130 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
2133 boolean thr = false;
2136 if (gcol.isAboveThreshold())
2140 tt += "Thresholded (Above " + gcol.getThreshold() + ") ";
2142 if (gcol.isBelowThreshold())
2146 tt += "Thresholded (Below " + gcol.getThreshold() + ") ";
2148 if (gcol.isColourByLabel())
2150 tt = "Coloured by label text. " + tt;
2160 Color newColor = gcol.getMaxColour();
2161 comp.setBackground(newColor);
2162 // System.err.println("Width is " + w / 2);
2163 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
2164 comp.setIcon(ficon);
2165 // tt+="RGB value: Max (" + newColor.getRed() + ", "
2166 // + newColor.getGreen() + ", " + newColor.getBlue()
2167 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
2168 // + ", " + minCol.getBlue() + ")");
2170 comp.setHorizontalAlignment(SwingConstants.CENTER);
2172 if (tt.length() > 0)
2174 if (comp.getToolTipText() == null)
2176 comp.setToolTipText(tt);
2180 comp.setToolTipText(tt + " " + comp.getToolTipText());
2186 class FeatureIcon implements Icon
2188 FeatureColourI gcol;
2192 boolean midspace = false;
2194 int width = 50, height = 20;
2196 int s1, e1; // start and end of midpoint band for thresholded symbol
2198 Color mpcolour = Color.white;
2200 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2220 public int getIconWidth()
2226 public int getIconHeight()
2232 public void paintIcon(Component c, Graphics g, int x, int y)
2235 if (gcol.isColourByLabel())
2238 g.fillRect(0, 0, width, height);
2239 // need an icon here.
2240 g.setColor(gcol.getMaxColour());
2242 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2244 // g.setFont(g.getFont().deriveFont(
2245 // AffineTransform.getScaleInstance(
2246 // width/g.getFontMetrics().stringWidth("Label"),
2247 // height/g.getFontMetrics().getHeight())));
2249 g.drawString(MessageManager.getString("label.label"), 0, 0);
2254 Color minCol = gcol.getMinColour();
2256 g.fillRect(0, 0, s1, height);
2259 g.setColor(Color.white);
2260 g.fillRect(s1, 0, e1 - s1, height);
2262 g.setColor(gcol.getMaxColour());
2263 g.fillRect(0, e1, width - e1, height);
2268 class ColorEditor extends AbstractCellEditor
2269 implements TableCellEditor, ActionListener
2273 FeatureColourI currentColor;
2275 FeatureColourChooser chooser;
2281 JColorChooser colorChooser;
2285 protected static final String EDIT = "edit";
2287 int selectedRow = 0;
2289 public ColorEditor(FeatureSettings me)
2292 // Set up the editor (from the table's point of view),
2293 // which is a button.
2294 // This button brings up the color chooser dialog,
2295 // which is the editor from the user's point of view.
2296 button = new JButton();
2297 button.setActionCommand(EDIT);
2298 button.addActionListener(this);
2299 button.setBorderPainted(false);
2300 // Set up the dialog that the button brings up.
2301 colorChooser = new JColorChooser();
2302 dialog = JColorChooser.createDialog(button, "Select new Colour", true, // modal
2303 colorChooser, this, // OK button handler
2304 null); // no CANCEL button handler
2308 * Handles events from the editor button and from the dialog's OK button.
2311 public void actionPerformed(ActionEvent e)
2314 if (EDIT.equals(e.getActionCommand()))
2316 // The user has clicked the cell, so
2317 // bring up the dialog.
2318 if (currentColor.isSimpleColour())
2320 // bring up simple color chooser
2321 button.setBackground(currentColor.getColour());
2322 colorChooser.setColor(currentColor.getColour());
2323 dialog.setVisible(true);
2327 // bring up graduated chooser.
2328 chooser = new FeatureColourChooser(me.fr, type);
2329 chooser.setRequestFocusEnabled(true);
2330 chooser.requestFocus();
2331 chooser.addActionListener(this);
2333 // Make the renderer reappear.
2334 fireEditingStopped();
2338 { // User pressed dialog's "OK" button.
2339 if (currentColor.isSimpleColour())
2341 currentColor = new FeatureColour(colorChooser.getColor());
2345 currentColor = chooser.getLastColour();
2347 me.table.setValueAt(getCellEditorValue(), selectedRow, 1);
2348 fireEditingStopped();
2349 me.table.validate();
2353 // Implement the one CellEditor method that AbstractCellEditor doesn't.
2355 public Object getCellEditorValue()
2357 return currentColor;
2360 // Implement the one method defined by TableCellEditor.
2362 public Component getTableCellEditorComponent(JTable table, Object value,
2363 boolean isSelected, int row, int column)
2365 currentColor = (FeatureColourI) value;
2366 this.selectedRow = row;
2367 type = me.table.getValueAt(row, 0).toString();
2368 button.setOpaque(true);
2369 button.setBackground(me.getBackground());
2370 if (!currentColor.isSimpleColour())
2372 JLabel btn = new JLabel();
2373 btn.setSize(button.getSize());
2374 FeatureSettings.renderGraduatedColor(btn, currentColor);
2375 button.setBackground(btn.getBackground());
2376 button.setIcon(btn.getIcon());
2377 button.setText(btn.getText());
2382 button.setIcon(null);
2383 button.setBackground(currentColor.getColour());