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.gui.Help.HelpId;
29 import jalview.io.JalviewFileChooser;
30 import jalview.io.JalviewFileView;
31 import jalview.schemabinding.version2.JalviewUserColours;
32 import jalview.schemes.FeatureColour;
33 import jalview.util.Format;
34 import jalview.util.MessageManager;
35 import jalview.util.Platform;
36 import jalview.util.QuickSort;
37 import jalview.viewmodel.AlignmentViewport;
38 import jalview.ws.dbsources.das.api.jalviewSourceI;
40 import java.awt.BorderLayout;
41 import java.awt.Color;
42 import java.awt.Component;
43 import java.awt.Dimension;
45 import java.awt.Graphics;
46 import java.awt.GridLayout;
47 import java.awt.Rectangle;
48 import java.awt.event.ActionEvent;
49 import java.awt.event.ActionListener;
50 import java.awt.event.ItemEvent;
51 import java.awt.event.ItemListener;
52 import java.awt.event.MouseAdapter;
53 import java.awt.event.MouseEvent;
54 import java.awt.event.MouseMotionAdapter;
55 import java.beans.PropertyChangeEvent;
56 import java.beans.PropertyChangeListener;
58 import java.io.FileInputStream;
59 import java.io.FileOutputStream;
60 import java.io.InputStreamReader;
61 import java.io.OutputStreamWriter;
62 import java.io.PrintWriter;
63 import java.util.Arrays;
64 import java.util.HashSet;
65 import java.util.Hashtable;
66 import java.util.Iterator;
67 import java.util.List;
70 import java.util.Vector;
72 import javax.help.HelpSetException;
73 import javax.swing.AbstractCellEditor;
74 import javax.swing.BorderFactory;
75 import javax.swing.Icon;
76 import javax.swing.JButton;
77 import javax.swing.JCheckBox;
78 import javax.swing.JCheckBoxMenuItem;
79 import javax.swing.JColorChooser;
80 import javax.swing.JDialog;
81 import javax.swing.JInternalFrame;
82 import javax.swing.JLabel;
83 import javax.swing.JLayeredPane;
84 import javax.swing.JMenuItem;
85 import javax.swing.JPanel;
86 import javax.swing.JPopupMenu;
87 import javax.swing.JScrollPane;
88 import javax.swing.JSlider;
89 import javax.swing.JTabbedPane;
90 import javax.swing.JTable;
91 import javax.swing.ListSelectionModel;
92 import javax.swing.SwingConstants;
93 import javax.swing.SwingUtilities;
94 import javax.swing.event.ChangeEvent;
95 import javax.swing.event.ChangeListener;
96 import javax.swing.table.AbstractTableModel;
97 import javax.swing.table.TableCellEditor;
98 import javax.swing.table.TableCellRenderer;
100 public class FeatureSettings extends JPanel
101 implements FeatureSettingsControllerI
103 DasSourceBrowser dassourceBrowser;
105 jalview.ws.DasSequenceFeatureFetcher dasFeatureFetcher;
107 JPanel settingsPane = new JPanel();
109 JPanel dasSettingsPane = new JPanel();
111 final FeatureRenderer fr;
113 public final AlignFrame af;
115 Object[][] originalData;
117 private float originalTransparency;
119 final JInternalFrame frame;
121 JScrollPane scrollPane = new JScrollPane();
127 JSlider transparency = new JSlider();
129 JPanel transPanel = new JPanel(new GridLayout(1, 2));
131 private static final int MIN_WIDTH = 400;
133 private static final int MIN_HEIGHT = 400;
136 * when true, constructor is still executing - so ignore UI events
138 protected volatile boolean inConstruction = true;
145 public FeatureSettings(AlignFrame af)
148 fr = af.getFeatureRenderer();
149 // allow transparency to be recovered
150 transparency.setMaximum(100
151 - (int) ((originalTransparency = fr.getTransparency()) * 100));
156 } catch (Exception ex)
158 ex.printStackTrace();
164 public String getToolTipText(MouseEvent e)
166 if (table.columnAtPoint(e.getPoint()) == 0)
169 * Tooltip for feature name only
171 return JvSwingUtils.wrapTooltip(true, MessageManager
172 .getString("label.feature_settings_click_drag"));
177 table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
178 table.setFont(new Font("Verdana", Font.PLAIN, 12));
179 table.setDefaultRenderer(Color.class, new ColorRenderer());
181 table.setDefaultEditor(Color.class, new ColorEditor(this));
183 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
184 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
185 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
187 table.addMouseListener(new MouseAdapter()
190 public void mousePressed(MouseEvent evt)
192 selectedRow = table.rowAtPoint(evt.getPoint());
193 if (evt.isPopupTrigger())
195 popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
196 table.getValueAt(selectedRow, 1), fr.getMinMax(),
197 evt.getX(), evt.getY());
199 else if (evt.getClickCount() == 2)
201 boolean invertSelection = evt.isAltDown();
202 boolean toggleSelection = Platform.isControlDown(evt);
203 boolean extendSelection = evt.isShiftDown();
204 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
205 invertSelection, extendSelection, toggleSelection,
206 (String) table.getValueAt(selectedRow, 0));
210 // isPopupTrigger fires on mouseReleased on Windows
212 public void mouseReleased(MouseEvent evt)
214 selectedRow = table.rowAtPoint(evt.getPoint());
215 if (evt.isPopupTrigger())
217 popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
218 table.getValueAt(selectedRow, 1), fr.getMinMax(),
219 evt.getX(), evt.getY());
224 table.addMouseMotionListener(new MouseMotionAdapter()
227 public void mouseDragged(MouseEvent evt)
229 int newRow = table.rowAtPoint(evt.getPoint());
230 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
233 * reposition 'selectedRow' to 'newRow' (the dragged to location)
234 * this could be more than one row away for a very fast drag action
235 * so just swap it with adjacent rows until we get it there
237 Object[][] data = ((FeatureTableModel) table.getModel())
239 int direction = newRow < selectedRow ? -1 : 1;
240 for (int i = selectedRow; i != newRow; i += direction)
242 Object[] temp = data[i];
243 data[i] = data[i + direction];
244 data[i + direction] = temp;
246 updateFeatureRenderer(data);
248 selectedRow = newRow;
252 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
253 // MessageManager.getString("label.feature_settings_click_drag")));
254 scrollPane.setViewportView(table);
256 dassourceBrowser = new DasSourceBrowser(this);
257 dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
259 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
261 fr.findAllFeatures(true); // display everything!
264 discoverAllFeatureData();
265 final PropertyChangeListener change;
266 final FeatureSettings fs = this;
267 fr.addPropertyChangeListener(change = new PropertyChangeListener()
270 public void propertyChange(PropertyChangeEvent evt)
272 if (!fs.resettingTable && !fs.handlingUpdate)
274 fs.handlingUpdate = true;
275 fs.resetTable(null); // new groups may be added with new seuqence
276 // feature types only
277 fs.handlingUpdate = false;
283 frame = new JInternalFrame();
284 frame.setContentPane(this);
285 if (Platform.isAMac())
287 Desktop.addInternalFrame(frame,
288 MessageManager.getString("label.sequence_feature_settings"),
293 Desktop.addInternalFrame(frame,
294 MessageManager.getString("label.sequence_feature_settings"),
297 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
299 frame.addInternalFrameListener(
300 new javax.swing.event.InternalFrameAdapter()
303 public void internalFrameClosed(
304 javax.swing.event.InternalFrameEvent evt)
306 fr.removePropertyChangeListener(change);
307 dassourceBrowser.fs = null;
310 frame.setLayer(JLayeredPane.PALETTE_LAYER);
311 inConstruction = false;
314 protected void popupSort(final int selectedRow, final String type,
315 final Object typeCol, final Map<String, float[][]> minmax, int x,
318 final FeatureColourI featureColour = (FeatureColourI) typeCol;
320 JPopupMenu men = new JPopupMenu(MessageManager
321 .formatMessage("label.settings_for_param", new String[]
323 JMenuItem scr = new JMenuItem(
324 MessageManager.getString("label.sort_by_score"));
326 final FeatureSettings me = this;
327 scr.addActionListener(new ActionListener()
331 public void actionPerformed(ActionEvent e)
334 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
339 JMenuItem dens = new JMenuItem(
340 MessageManager.getString("label.sort_by_density"));
341 dens.addActionListener(new ActionListener()
345 public void actionPerformed(ActionEvent e)
348 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
356 final float[][] typeMinMax = minmax.get(type);
358 * final JCheckBoxMenuItem chb = new JCheckBoxMenuItem("Vary Height"); //
359 * this is broken at the moment and isn't that useful anyway!
360 * chb.setSelected(minmax.get(type) != null); chb.addActionListener(new
363 * public void actionPerformed(ActionEvent e) {
364 * chb.setState(chb.getState()); if (chb.getState()) { minmax.put(type,
365 * null); } else { minmax.put(type, typeMinMax); } }
371 if (typeMinMax != null && typeMinMax[0] != null)
373 // if (table.getValueAt(row, column));
374 // graduated colourschemes for those where minmax exists for the
375 // positional features
376 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
378 mxcol.setSelected(!featureColour.isSimpleColour());
380 mxcol.addActionListener(new ActionListener()
382 JColorChooser colorChooser;
385 public void actionPerformed(ActionEvent e)
387 if (e.getSource() == mxcol)
389 if (featureColour.isSimpleColour())
391 FeatureColourChooser fc = new FeatureColourChooser(me.fr,
393 fc.addActionListener(this);
397 // bring up simple color chooser
398 colorChooser = new JColorChooser();
399 JDialog dialog = JColorChooser.createDialog(me,
400 "Select new Colour", true, // modal
401 colorChooser, this, // OK button handler
402 null); // no CANCEL button handler
403 colorChooser.setColor(featureColour.getMaxColour());
404 dialog.setVisible(true);
409 if (e.getSource() instanceof FeatureColourChooser)
411 FeatureColourChooser fc = (FeatureColourChooser) e
413 table.setValueAt(fc.getLastColour(), selectedRow, 1);
418 // probably the color chooser!
419 table.setValueAt(new FeatureColour(colorChooser.getColor()),
422 me.updateFeatureRenderer(
423 ((FeatureTableModel) table.getModel()).getData(),
432 JMenuItem selCols = new JMenuItem(
433 MessageManager.getString("label.select_columns_containing"));
434 selCols.addActionListener(new ActionListener()
437 public void actionPerformed(ActionEvent arg0)
439 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
443 JMenuItem clearCols = new JMenuItem(MessageManager
444 .getString("label.select_columns_not_containing"));
445 clearCols.addActionListener(new ActionListener()
448 public void actionPerformed(ActionEvent arg0)
450 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
454 JMenuItem hideCols = new JMenuItem(
455 MessageManager.getString("label.hide_columns_containing"));
456 hideCols.addActionListener(new ActionListener()
459 public void actionPerformed(ActionEvent arg0)
461 fr.ap.alignFrame.hideFeatureColumns(type, true);
464 JMenuItem hideOtherCols = new JMenuItem(
465 MessageManager.getString("label.hide_columns_not_containing"));
466 hideOtherCols.addActionListener(new ActionListener()
469 public void actionPerformed(ActionEvent arg0)
471 fr.ap.alignFrame.hideFeatureColumns(type, false);
477 men.add(hideOtherCols);
478 men.show(table, x, y);
482 * true when Feature Settings are updating from feature renderer
484 private boolean handlingUpdate = false;
487 * holds {featureCount, totalExtent} for each feature type
489 Map<String, float[]> typeWidth = null;
492 synchronized public void discoverAllFeatureData()
494 Set<String> allGroups = new HashSet<>();
495 AlignmentI alignment = af.getViewport().getAlignment();
497 for (int i = 0; i < alignment.getHeight(); i++)
499 SequenceI seq = alignment.getSequenceAt(i);
500 for (String group : seq.getFeatures().getFeatureGroups(true))
502 if (group != null && !allGroups.contains(group))
504 allGroups.add(group);
505 checkGroupState(group);
516 * Synchronise gui group list and check visibility of group
519 * @return true if group is visible
521 private boolean checkGroupState(String group)
523 boolean visible = fr.checkGroupVisibility(group, true);
525 for (int g = 0; g < groupPanel.getComponentCount(); g++)
527 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
529 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
534 final String grp = group;
535 final JCheckBox check = new JCheckBox(group, visible);
536 check.setFont(new Font("Serif", Font.BOLD, 12));
537 check.setToolTipText(group);
538 check.addItemListener(new ItemListener()
541 public void itemStateChanged(ItemEvent evt)
543 fr.setGroupVisibility(check.getText(), check.isSelected());
544 resetTable(new String[] { grp });
545 af.alignPanel.paintAlignment(true, true);
548 groupPanel.add(check);
552 boolean resettingTable = false;
554 synchronized void resetTable(String[] groupChanged)
560 resettingTable = true;
561 typeWidth = new Hashtable<>();
562 // TODO: change avWidth calculation to 'per-sequence' average and use long
565 Set<String> displayableTypes = new HashSet<>();
566 Set<String> foundGroups = new HashSet<>();
569 * determine which feature types may be visible depending on
570 * which groups are selected, and recompute average width data
572 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
575 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
578 * get the sequence's groups for positional features
579 * and keep track of which groups are visible
581 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
582 Set<String> visibleGroups = new HashSet<>();
583 for (String group : groups)
585 if (group == null || checkGroupState(group))
587 visibleGroups.add(group);
590 foundGroups.addAll(groups);
593 * get distinct feature types for visible groups
594 * record distinct visible types, and their count and total length
596 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
597 visibleGroups.toArray(new String[visibleGroups.size()]));
598 for (String type : types)
600 displayableTypes.add(type);
601 float[] avWidth = typeWidth.get(type);
604 avWidth = new float[2];
605 typeWidth.put(type, avWidth);
607 // todo this could include features with a non-visible group
608 // - do we greatly care?
609 // todo should we include non-displayable features here, and only
610 // update when features are added?
611 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
612 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
616 Object[][] data = new Object[displayableTypes.size()][3];
619 if (fr.hasRenderOrder())
623 fr.findAllFeatures(groupChanged != null); // prod to update
624 // colourschemes. but don't
626 // First add the checks in the previous render order,
627 // in case the window has been closed and reopened
629 List<String> frl = fr.getRenderOrder();
630 for (int ro = frl.size() - 1; ro > -1; ro--)
632 String type = frl.get(ro);
634 if (!displayableTypes.contains(type))
639 data[dataIndex][0] = type;
640 data[dataIndex][1] = fr.getFeatureStyle(type);
641 data[dataIndex][2] = new Boolean(
642 af.getViewport().getFeaturesDisplayed().isVisible(type));
644 displayableTypes.remove(type);
649 * process any extra features belonging only to
650 * a group which was just selected
652 while (!displayableTypes.isEmpty())
654 String type = displayableTypes.iterator().next();
655 data[dataIndex][0] = type;
657 data[dataIndex][1] = fr.getFeatureStyle(type);
658 if (data[dataIndex][1] == null)
660 // "Colour has been updated in another view!!"
661 fr.clearRenderOrder();
665 data[dataIndex][2] = new Boolean(true);
667 displayableTypes.remove(type);
670 if (originalData == null)
672 originalData = new Object[data.length][3];
673 for (int i = 0; i < data.length; i++)
675 System.arraycopy(data[i], 0, originalData[i], 0, 3);
680 updateOriginalData(data);
683 table.setModel(new FeatureTableModel(data));
684 table.getColumnModel().getColumn(0).setPreferredWidth(200);
686 groupPanel.setLayout(
687 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
688 pruneGroups(foundGroups);
689 groupPanel.validate();
691 updateFeatureRenderer(data, groupChanged != null);
692 resettingTable = false;
696 * Updates 'originalData' (used for restore on Cancel) if we detect that
697 * changes have been made outwith this dialog
699 * <li>a new feature type added (and made visible)</li>
700 * <li>a feature colour changed (in the Amend Features dialog)</li>
705 protected void updateOriginalData(Object[][] foundData)
707 // todo LinkedHashMap instead of Object[][] would be nice
709 Object[][] currentData = ((FeatureTableModel) table.getModel())
711 for (Object[] row : foundData)
713 String type = (String) row[0];
714 boolean found = false;
715 for (Object[] current : currentData)
717 if (type.equals(current[0]))
721 * currently dependent on object equality here;
722 * really need an equals method on FeatureColour
724 if (!row[1].equals(current[1]))
727 * feature colour has changed externally - update originalData
729 for (Object[] original : originalData)
731 if (type.equals(original[0]))
733 original[1] = row[1];
744 * new feature detected - add to original data (on top)
746 Object[][] newData = new Object[originalData.length + 1][3];
747 for (int i = 0; i < originalData.length; i++)
749 System.arraycopy(originalData[i], 0, newData[i + 1], 0, 3);
752 originalData = newData;
758 * Remove from the groups panel any checkboxes for groups that are not in the
759 * foundGroups set. This enables removing a group from the display when the
760 * last feature in that group is deleted.
764 protected void pruneGroups(Set<String> foundGroups)
766 for (int g = 0; g < groupPanel.getComponentCount(); g++)
768 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
769 if (!foundGroups.contains(checkbox.getText()))
771 groupPanel.remove(checkbox);
777 * reorder data based on the featureRenderers global priority list.
781 private void ensureOrder(Object[][] data)
783 boolean sort = false;
784 float[] order = new float[data.length];
785 for (int i = 0; i < order.length; i++)
787 order[i] = fr.getOrder(data[i][0].toString());
790 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
794 sort = sort || order[i - 1] > order[i];
799 jalview.util.QuickSort.sort(order, data);
805 JalviewFileChooser chooser = new JalviewFileChooser("fc",
806 "Sequence Feature Colours");
807 chooser.setFileView(new JalviewFileView());
808 chooser.setDialogTitle(
809 MessageManager.getString("label.load_feature_colours"));
810 chooser.setToolTipText(MessageManager.getString("action.load"));
812 int value = chooser.showOpenDialog(this);
814 if (value == JalviewFileChooser.APPROVE_OPTION)
816 File file = chooser.getSelectedFile();
820 InputStreamReader in = new InputStreamReader(
821 new FileInputStream(file), "UTF-8");
823 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
825 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
828 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
831 Color mincol = null, maxcol = null;
834 mincol = new Color(Integer.parseInt(newcol.getMinRGB(), 16));
835 maxcol = new Color(Integer.parseInt(newcol.getRGB(), 16));
837 } catch (Exception e)
839 Cache.log.warn("Couldn't parse out graduated feature color.",
842 FeatureColourI gcol = new FeatureColour(mincol, maxcol,
843 newcol.getMin(), newcol.getMax());
844 if (newcol.hasAutoScale())
846 gcol.setAutoScaled(newcol.getAutoScale());
848 if (newcol.hasColourByLabel())
850 gcol.setColourByLabel(newcol.getColourByLabel());
852 if (newcol.hasThreshold())
854 gcol.setThreshold(newcol.getThreshold());
856 if (newcol.getThreshType().length() > 0)
858 String ttyp = newcol.getThreshType();
859 if (ttyp.equalsIgnoreCase("ABOVE"))
861 gcol.setAboveThreshold(true);
863 if (ttyp.equalsIgnoreCase("BELOW"))
865 gcol.setBelowThreshold(true);
868 fr.setColour(name = newcol.getName(), gcol);
872 Color color = new Color(
873 Integer.parseInt(jucs.getColour(i).getRGB(), 16));
874 fr.setColour(name = jucs.getColour(i).getName(),
875 new FeatureColour(color));
877 fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
882 Object[][] data = ((FeatureTableModel) table.getModel())
885 updateFeatureRenderer(data, false);
888 } catch (Exception ex)
890 System.out.println("Error loading User Colour File\n" + ex);
897 JalviewFileChooser chooser = new JalviewFileChooser("fc",
898 "Sequence Feature Colours");
899 chooser.setFileView(new JalviewFileView());
900 chooser.setDialogTitle(
901 MessageManager.getString("label.save_feature_colours"));
902 chooser.setToolTipText(MessageManager.getString("action.save"));
904 int value = chooser.showSaveDialog(this);
906 if (value == JalviewFileChooser.APPROVE_OPTION)
908 String choice = chooser.getSelectedFile().getPath();
909 jalview.schemabinding.version2.JalviewUserColours ucs = new jalview.schemabinding.version2.JalviewUserColours();
910 ucs.setSchemeName("Sequence Features");
913 PrintWriter out = new PrintWriter(new OutputStreamWriter(
914 new FileOutputStream(choice), "UTF-8"));
916 Set<String> fr_colours = fr.getAllFeatureColours();
917 Iterator<String> e = fr_colours.iterator();
918 float[] sortOrder = new float[fr_colours.size()];
919 String[] sortTypes = new String[fr_colours.size()];
923 sortTypes[i] = e.next();
924 sortOrder[i] = fr.getOrder(sortTypes[i]);
927 QuickSort.sort(sortOrder, sortTypes);
929 for (i = 0; i < sortTypes.length; i++)
931 jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
932 col.setName(sortTypes[i]);
933 FeatureColourI fcol = fr.getFeatureStyle(sortTypes[i]);
934 if (fcol.isSimpleColour())
936 col.setRGB(Format.getHexString(fcol.getColour()));
940 col.setRGB(Format.getHexString(fcol.getMaxColour()));
941 col.setMin(fcol.getMin());
942 col.setMax(fcol.getMax());
944 jalview.util.Format.getHexString(fcol.getMinColour()));
945 col.setAutoScale(fcol.isAutoScaled());
946 col.setThreshold(fcol.getThreshold());
947 col.setColourByLabel(fcol.isColourByLabel());
948 col.setThreshType(fcol.isAboveThreshold() ? "ABOVE"
949 : (fcol.isBelowThreshold() ? "BELOW" : "NONE"));
955 } catch (Exception ex)
957 ex.printStackTrace();
962 public void invertSelection()
964 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
965 for (int i = 0; i < data.length; i++)
967 data[i][2] = !(Boolean) data[i][2];
969 updateFeatureRenderer(data, true);
973 public void orderByAvWidth()
975 if (table == null || table.getModel() == null)
979 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
980 float[] width = new float[data.length];
984 for (int i = 0; i < data.length; i++)
986 awidth = typeWidth.get(data[i][0]);
989 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
990 // weight - but have to make per
991 // sequence, too (awidth[2])
992 // if (width[i]==1) // hack to distinguish single width sequences.
1004 boolean sort = false;
1005 for (int i = 0; i < width.length; i++)
1007 // awidth = (float[]) typeWidth.get(data[i][0]);
1010 width[i] = fr.getOrder(data[i][0].toString());
1013 width[i] = fr.setOrder(data[i][0].toString(), i / data.length);
1018 width[i] /= max; // normalize
1019 fr.setOrder(data[i][0].toString(), width[i]); // store for later
1023 sort = sort || width[i - 1] > width[i];
1028 jalview.util.QuickSort.sort(width, data);
1029 // update global priority order
1032 updateFeatureRenderer(data, false);
1040 frame.setClosed(true);
1041 } catch (Exception exe)
1047 public void updateFeatureRenderer(Object[][] data)
1049 updateFeatureRenderer(data, true);
1053 * Update the priority order of features; only repaint if this changed the
1054 * order of visible features
1059 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1061 if (fr.setFeaturePriority(data, visibleNew))
1063 af.alignPanel.paintAlignment(true, true);
1067 int selectedRow = -1;
1069 JTabbedPane tabbedPane = new JTabbedPane();
1071 BorderLayout borderLayout1 = new BorderLayout();
1073 BorderLayout borderLayout2 = new BorderLayout();
1075 BorderLayout borderLayout3 = new BorderLayout();
1077 JPanel bigPanel = new JPanel();
1079 BorderLayout borderLayout4 = new BorderLayout();
1081 JButton invert = new JButton();
1083 JPanel buttonPanel = new JPanel();
1085 JButton cancel = new JButton();
1087 JButton ok = new JButton();
1089 JButton loadColours = new JButton();
1091 JButton saveColours = new JButton();
1093 JPanel dasButtonPanel = new JPanel();
1095 JButton fetchDAS = new JButton();
1097 JButton saveDAS = new JButton();
1099 JButton cancelDAS = new JButton();
1101 JButton optimizeOrder = new JButton();
1103 JButton sortByScore = new JButton();
1105 JButton sortByDens = new JButton();
1107 JButton help = new JButton();
1109 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1111 private void jbInit() throws Exception
1113 this.setLayout(borderLayout1);
1114 settingsPane.setLayout(borderLayout2);
1115 dasSettingsPane.setLayout(borderLayout3);
1116 bigPanel.setLayout(borderLayout4);
1118 groupPanel = new JPanel();
1119 bigPanel.add(groupPanel, BorderLayout.NORTH);
1121 invert.setFont(JvSwingUtils.getLabelFont());
1122 invert.setText(MessageManager.getString("label.invert_selection"));
1123 invert.addActionListener(new ActionListener()
1126 public void actionPerformed(ActionEvent e)
1131 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1132 optimizeOrder.setText(MessageManager.getString("label.optimise_order"));
1133 optimizeOrder.addActionListener(new ActionListener()
1136 public void actionPerformed(ActionEvent e)
1141 sortByScore.setFont(JvSwingUtils.getLabelFont());
1143 .setText(MessageManager.getString("label.seq_sort_by_score"));
1144 sortByScore.addActionListener(new ActionListener()
1147 public void actionPerformed(ActionEvent e)
1149 af.avc.sortAlignmentByFeatureScore(null);
1152 sortByDens.setFont(JvSwingUtils.getLabelFont());
1154 MessageManager.getString("label.sequence_sort_by_density"));
1155 sortByDens.addActionListener(new ActionListener()
1158 public void actionPerformed(ActionEvent e)
1160 af.avc.sortAlignmentByFeatureDensity(null);
1163 help.setFont(JvSwingUtils.getLabelFont());
1164 help.setText(MessageManager.getString("action.help"));
1165 help.addActionListener(new ActionListener()
1168 public void actionPerformed(ActionEvent e)
1172 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1173 } catch (HelpSetException e1)
1175 e1.printStackTrace();
1179 help.setFont(JvSwingUtils.getLabelFont());
1180 help.setText(MessageManager.getString("action.help"));
1181 help.addActionListener(new ActionListener()
1184 public void actionPerformed(ActionEvent e)
1188 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1189 } catch (HelpSetException e1)
1191 e1.printStackTrace();
1195 cancel.setFont(JvSwingUtils.getLabelFont());
1196 cancel.setText(MessageManager.getString("action.cancel"));
1197 cancel.addActionListener(new ActionListener()
1200 public void actionPerformed(ActionEvent e)
1202 fr.setTransparency(originalTransparency);
1203 updateFeatureRenderer(originalData);
1207 ok.setFont(JvSwingUtils.getLabelFont());
1208 ok.setText(MessageManager.getString("action.ok"));
1209 ok.addActionListener(new ActionListener()
1212 public void actionPerformed(ActionEvent e)
1217 loadColours.setFont(JvSwingUtils.getLabelFont());
1218 loadColours.setText(MessageManager.getString("label.load_colours"));
1219 loadColours.addActionListener(new ActionListener()
1222 public void actionPerformed(ActionEvent e)
1227 saveColours.setFont(JvSwingUtils.getLabelFont());
1228 saveColours.setText(MessageManager.getString("label.save_colours"));
1229 saveColours.addActionListener(new ActionListener()
1232 public void actionPerformed(ActionEvent e)
1237 transparency.addChangeListener(new ChangeListener()
1240 public void stateChanged(ChangeEvent evt)
1242 if (!inConstruction)
1244 fr.setTransparency((100 - transparency.getValue()) / 100f);
1245 af.alignPanel.paintAlignment(true,true);
1250 transparency.setMaximum(70);
1251 transparency.setToolTipText(
1252 MessageManager.getString("label.transparency_tip"));
1253 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1254 fetchDAS.addActionListener(new ActionListener()
1257 public void actionPerformed(ActionEvent e)
1259 fetchDAS_actionPerformed(e);
1262 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1263 saveDAS.addActionListener(new ActionListener()
1266 public void actionPerformed(ActionEvent e)
1268 saveDAS_actionPerformed(e);
1271 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1272 dasSettingsPane.setBorder(null);
1273 cancelDAS.setEnabled(false);
1274 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1275 cancelDAS.addActionListener(new ActionListener()
1278 public void actionPerformed(ActionEvent e)
1280 cancelDAS_actionPerformed(e);
1283 this.add(tabbedPane, java.awt.BorderLayout.CENTER);
1284 tabbedPane.addTab(MessageManager.getString("label.feature_settings"),
1286 tabbedPane.addTab(MessageManager.getString("label.das_settings"),
1288 bigPanel.add(transPanel, java.awt.BorderLayout.SOUTH);
1289 transbuttons.add(optimizeOrder);
1290 transbuttons.add(invert);
1291 transbuttons.add(sortByScore);
1292 transbuttons.add(sortByDens);
1293 transbuttons.add(help);
1294 JPanel sliderPanel = new JPanel();
1295 sliderPanel.add(transparency);
1296 transPanel.add(transparency);
1297 transPanel.add(transbuttons);
1298 buttonPanel.add(ok);
1299 buttonPanel.add(cancel);
1300 buttonPanel.add(loadColours);
1301 buttonPanel.add(saveColours);
1302 bigPanel.add(scrollPane, java.awt.BorderLayout.CENTER);
1303 dasSettingsPane.add(dasButtonPanel, java.awt.BorderLayout.SOUTH);
1304 dasButtonPanel.add(fetchDAS);
1305 dasButtonPanel.add(cancelDAS);
1306 dasButtonPanel.add(saveDAS);
1307 settingsPane.add(bigPanel, java.awt.BorderLayout.CENTER);
1308 settingsPane.add(buttonPanel, java.awt.BorderLayout.SOUTH);
1311 public void fetchDAS_actionPerformed(ActionEvent e)
1313 fetchDAS.setEnabled(false);
1314 cancelDAS.setEnabled(true);
1315 dassourceBrowser.setGuiEnabled(false);
1316 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1317 .getSelectedSources();
1318 doDasFeatureFetch(selectedSources, true, true);
1322 * get the features from selectedSources for all or the current selection
1324 * @param selectedSources
1325 * @param checkDbRefs
1326 * @param promptFetchDbRefs
1328 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1329 boolean checkDbRefs, boolean promptFetchDbRefs)
1331 SequenceI[] dataset, seqs;
1333 AlignmentViewport vp = af.getViewport();
1334 if (vp.getSelectionGroup() != null
1335 && vp.getSelectionGroup().getSize() > 0)
1337 iSize = vp.getSelectionGroup().getSize();
1338 dataset = new SequenceI[iSize];
1339 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1343 iSize = vp.getAlignment().getHeight();
1344 seqs = vp.getAlignment().getSequencesArray();
1347 dataset = new SequenceI[iSize];
1348 for (int i = 0; i < iSize; i++)
1350 dataset[i] = seqs[i].getDatasetSequence();
1353 cancelDAS.setEnabled(true);
1354 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1355 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1356 af.getViewport().setShowSequenceFeatures(true);
1357 af.showSeqFeatures.setSelected(true);
1361 * blocking call to initialise the das source browser
1363 public void initDasSources()
1365 dassourceBrowser.initDasSources();
1369 * examine the current list of das sources and return any matching the given
1370 * nicknames in sources
1373 * Vector of Strings to resolve to DAS source nicknames.
1374 * @return sources that are present in source list.
1376 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1378 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1382 * get currently selected das sources. ensure you have called initDasSources
1383 * before calling this.
1385 * @return vector of selected das source nicknames
1387 public Vector<jalviewSourceI> getSelectedSources()
1389 return dassourceBrowser.getSelectedSources();
1393 * properly initialise DAS fetcher and then initiate a new thread to fetch
1394 * features from the named sources (rather than any turned on by default)
1398 * if true then runs in same thread, otherwise passes to the Swing
1401 public void fetchDasFeatures(Vector<String> sources, boolean block)
1404 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1405 .resolveSourceNicknames(sources);
1406 if (resolved.size() == 0)
1408 resolved = dassourceBrowser.getSelectedSources();
1410 if (resolved.size() > 0)
1412 final List<jalviewSourceI> dassources = resolved;
1413 fetchDAS.setEnabled(false);
1414 // cancelDAS.setEnabled(true); doDasFetch does this.
1415 Runnable fetcher = new Runnable()
1421 doDasFeatureFetch(dassources, true, false);
1431 SwingUtilities.invokeLater(fetcher);
1436 public void saveDAS_actionPerformed(ActionEvent e)
1439 .saveProperties(jalview.bin.Cache.applicationProperties);
1442 public void complete()
1444 fetchDAS.setEnabled(true);
1445 cancelDAS.setEnabled(false);
1446 dassourceBrowser.setGuiEnabled(true);
1450 public void cancelDAS_actionPerformed(ActionEvent e)
1452 if (dasFeatureFetcher != null)
1454 dasFeatureFetcher.cancel();
1459 public void noDasSourceActive()
1462 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1463 MessageManager.getString("label.no_das_sources_selected_warn"),
1464 MessageManager.getString("label.no_das_sources_selected_title"),
1465 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1468 // ///////////////////////////////////////////////////////////////////////
1469 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1470 // ///////////////////////////////////////////////////////////////////////
1471 class FeatureTableModel extends AbstractTableModel
1473 FeatureTableModel(Object[][] data)
1478 private String[] columnNames = {
1479 MessageManager.getString("label.feature_type"),
1480 MessageManager.getString("action.colour"),
1481 MessageManager.getString("label.display") };
1483 private Object[][] data;
1485 public Object[][] getData()
1490 public void setData(Object[][] data)
1496 public int getColumnCount()
1498 return columnNames.length;
1501 public Object[] getRow(int row)
1507 public int getRowCount()
1513 public String getColumnName(int col)
1515 return columnNames[col];
1519 public Object getValueAt(int row, int col)
1521 return data[row][col];
1525 public Class getColumnClass(int c)
1527 return getValueAt(0, c).getClass();
1531 public boolean isCellEditable(int row, int col)
1533 return col == 0 ? false : true;
1537 public void setValueAt(Object value, int row, int col)
1539 data[row][col] = value;
1540 fireTableCellUpdated(row, col);
1541 updateFeatureRenderer(data);
1546 class ColorRenderer extends JLabel implements TableCellRenderer
1548 javax.swing.border.Border unselectedBorder = null;
1550 javax.swing.border.Border selectedBorder = null;
1552 final String baseTT = "Click to edit, right/apple click for menu.";
1554 public ColorRenderer()
1556 setOpaque(true); // MUST do this for background to show up.
1557 setHorizontalTextPosition(SwingConstants.CENTER);
1558 setVerticalTextPosition(SwingConstants.CENTER);
1562 public Component getTableCellRendererComponent(JTable tbl, Object color,
1563 boolean isSelected, boolean hasFocus, int row, int column)
1565 FeatureColourI cellColour = (FeatureColourI) color;
1566 // JLabel comp = new JLabel();
1570 // setBounds(getBounds());
1572 setToolTipText(baseTT);
1573 setBackground(tbl.getBackground());
1574 if (!cellColour.isSimpleColour())
1576 Rectangle cr = tbl.getCellRect(row, column, false);
1577 FeatureSettings.renderGraduatedColor(this, cellColour,
1578 (int) cr.getWidth(), (int) cr.getHeight());
1585 newColor = cellColour.getColour();
1586 setBackground(newColor);
1590 if (selectedBorder == null)
1592 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1593 tbl.getSelectionBackground());
1595 setBorder(selectedBorder);
1599 if (unselectedBorder == null)
1601 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1602 tbl.getBackground());
1604 setBorder(unselectedBorder);
1612 * update comp using rendering settings from gcol
1617 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1619 int w = comp.getWidth(), h = comp.getHeight();
1622 w = (int) comp.getPreferredSize().getWidth();
1623 h = (int) comp.getPreferredSize().getHeight();
1630 renderGraduatedColor(comp, gcol, w, h);
1633 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1636 boolean thr = false;
1639 if (gcol.isAboveThreshold())
1643 tt += "Thresholded (Above " + gcol.getThreshold() + ") ";
1645 if (gcol.isBelowThreshold())
1649 tt += "Thresholded (Below " + gcol.getThreshold() + ") ";
1651 if (gcol.isColourByLabel())
1653 tt = "Coloured by label text. " + tt;
1663 Color newColor = gcol.getMaxColour();
1664 comp.setBackground(newColor);
1665 // System.err.println("Width is " + w / 2);
1666 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1667 comp.setIcon(ficon);
1668 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1669 // + newColor.getGreen() + ", " + newColor.getBlue()
1670 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1671 // + ", " + minCol.getBlue() + ")");
1673 comp.setHorizontalAlignment(SwingConstants.CENTER);
1675 if (tt.length() > 0)
1677 if (comp.getToolTipText() == null)
1679 comp.setToolTipText(tt);
1683 comp.setToolTipText(tt + " " + comp.getToolTipText());
1689 class FeatureIcon implements Icon
1691 FeatureColourI gcol;
1695 boolean midspace = false;
1697 int width = 50, height = 20;
1699 int s1, e1; // start and end of midpoint band for thresholded symbol
1701 Color mpcolour = Color.white;
1703 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1723 public int getIconWidth()
1729 public int getIconHeight()
1735 public void paintIcon(Component c, Graphics g, int x, int y)
1738 if (gcol.isColourByLabel())
1741 g.fillRect(0, 0, width, height);
1742 // need an icon here.
1743 g.setColor(gcol.getMaxColour());
1745 g.setFont(new Font("Verdana", Font.PLAIN, 9));
1747 // g.setFont(g.getFont().deriveFont(
1748 // AffineTransform.getScaleInstance(
1749 // width/g.getFontMetrics().stringWidth("Label"),
1750 // height/g.getFontMetrics().getHeight())));
1752 g.drawString(MessageManager.getString("label.label"), 0, 0);
1757 Color minCol = gcol.getMinColour();
1759 g.fillRect(0, 0, s1, height);
1762 g.setColor(Color.white);
1763 g.fillRect(s1, 0, e1 - s1, height);
1765 g.setColor(gcol.getMaxColour());
1766 g.fillRect(0, e1, width - e1, height);
1771 class ColorEditor extends AbstractCellEditor
1772 implements TableCellEditor, ActionListener
1776 FeatureColourI currentColor;
1778 FeatureColourChooser chooser;
1784 JColorChooser colorChooser;
1788 protected static final String EDIT = "edit";
1790 int selectedRow = 0;
1792 public ColorEditor(FeatureSettings me)
1795 // Set up the editor (from the table's point of view),
1796 // which is a button.
1797 // This button brings up the color chooser dialog,
1798 // which is the editor from the user's point of view.
1799 button = new JButton();
1800 button.setActionCommand(EDIT);
1801 button.addActionListener(this);
1802 button.setBorderPainted(false);
1803 // Set up the dialog that the button brings up.
1804 colorChooser = new JColorChooser();
1805 dialog = JColorChooser.createDialog(button, "Select new Colour", true, // modal
1806 colorChooser, this, // OK button handler
1807 null); // no CANCEL button handler
1811 * Handles events from the editor button and from the dialog's OK button.
1814 public void actionPerformed(ActionEvent e)
1817 if (EDIT.equals(e.getActionCommand()))
1819 // The user has clicked the cell, so
1820 // bring up the dialog.
1821 if (currentColor.isSimpleColour())
1823 // bring up simple color chooser
1824 button.setBackground(currentColor.getColour());
1825 colorChooser.setColor(currentColor.getColour());
1826 dialog.setVisible(true);
1830 // bring up graduated chooser.
1831 chooser = new FeatureColourChooser(me.fr, type);
1832 chooser.setRequestFocusEnabled(true);
1833 chooser.requestFocus();
1834 chooser.addActionListener(this);
1836 // Make the renderer reappear.
1837 fireEditingStopped();
1841 { // User pressed dialog's "OK" button.
1842 if (currentColor.isSimpleColour())
1844 currentColor = new FeatureColour(colorChooser.getColor());
1848 currentColor = chooser.getLastColour();
1850 me.table.setValueAt(getCellEditorValue(), selectedRow, 1);
1851 fireEditingStopped();
1852 me.table.validate();
1856 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1858 public Object getCellEditorValue()
1860 return currentColor;
1863 // Implement the one method defined by TableCellEditor.
1865 public Component getTableCellEditorComponent(JTable table, Object value,
1866 boolean isSelected, int row, int column)
1868 currentColor = (FeatureColourI) value;
1869 this.selectedRow = row;
1870 type = me.table.getValueAt(row, 0).toString();
1871 button.setOpaque(true);
1872 button.setBackground(me.getBackground());
1873 if (!currentColor.isSimpleColour())
1875 JLabel btn = new JLabel();
1876 btn.setSize(button.getSize());
1877 FeatureSettings.renderGraduatedColor(btn, currentColor);
1878 button.setBackground(btn.getBackground());
1879 button.setIcon(btn.getIcon());
1880 button.setText(btn.getText());
1885 button.setIcon(null);
1886 button.setBackground(currentColor.getColour());