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 for (int i = 0; i < table.getRowCount(); i++)
966 Boolean value = (Boolean) table.getValueAt(i, 2);
968 table.setValueAt(new Boolean(!value.booleanValue()), i, 2);
972 public void orderByAvWidth()
974 if (table == null || table.getModel() == null)
978 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
979 float[] width = new float[data.length];
983 for (int i = 0; i < data.length; i++)
985 awidth = typeWidth.get(data[i][0]);
988 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
989 // weight - but have to make per
990 // sequence, too (awidth[2])
991 // if (width[i]==1) // hack to distinguish single width sequences.
1003 boolean sort = false;
1004 for (int i = 0; i < width.length; i++)
1006 // awidth = (float[]) typeWidth.get(data[i][0]);
1009 width[i] = fr.getOrder(data[i][0].toString());
1012 width[i] = fr.setOrder(data[i][0].toString(), i / data.length);
1017 width[i] /= max; // normalize
1018 fr.setOrder(data[i][0].toString(), width[i]); // store for later
1022 sort = sort || width[i - 1] > width[i];
1027 jalview.util.QuickSort.sort(width, data);
1028 // update global priority order
1031 updateFeatureRenderer(data, false);
1039 frame.setClosed(true);
1040 } catch (Exception exe)
1046 public void updateFeatureRenderer(Object[][] data)
1048 updateFeatureRenderer(data, true);
1052 * Update the priority order of features; only repaint if this changed the
1053 * order of visible features
1058 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1060 if (fr.setFeaturePriority(data, visibleNew))
1062 af.alignPanel.paintAlignment(true, true);
1066 int selectedRow = -1;
1068 JTabbedPane tabbedPane = new JTabbedPane();
1070 BorderLayout borderLayout1 = new BorderLayout();
1072 BorderLayout borderLayout2 = new BorderLayout();
1074 BorderLayout borderLayout3 = new BorderLayout();
1076 JPanel bigPanel = new JPanel();
1078 BorderLayout borderLayout4 = new BorderLayout();
1080 JButton invert = new JButton();
1082 JPanel buttonPanel = new JPanel();
1084 JButton cancel = new JButton();
1086 JButton ok = new JButton();
1088 JButton loadColours = new JButton();
1090 JButton saveColours = new JButton();
1092 JPanel dasButtonPanel = new JPanel();
1094 JButton fetchDAS = new JButton();
1096 JButton saveDAS = new JButton();
1098 JButton cancelDAS = new JButton();
1100 JButton optimizeOrder = new JButton();
1102 JButton sortByScore = new JButton();
1104 JButton sortByDens = new JButton();
1106 JButton help = new JButton();
1108 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1110 private void jbInit() throws Exception
1112 this.setLayout(borderLayout1);
1113 settingsPane.setLayout(borderLayout2);
1114 dasSettingsPane.setLayout(borderLayout3);
1115 bigPanel.setLayout(borderLayout4);
1117 groupPanel = new JPanel();
1118 bigPanel.add(groupPanel, BorderLayout.NORTH);
1120 invert.setFont(JvSwingUtils.getLabelFont());
1121 invert.setText(MessageManager.getString("label.invert_selection"));
1122 invert.addActionListener(new ActionListener()
1125 public void actionPerformed(ActionEvent e)
1130 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1131 optimizeOrder.setText(MessageManager.getString("label.optimise_order"));
1132 optimizeOrder.addActionListener(new ActionListener()
1135 public void actionPerformed(ActionEvent e)
1140 sortByScore.setFont(JvSwingUtils.getLabelFont());
1142 .setText(MessageManager.getString("label.seq_sort_by_score"));
1143 sortByScore.addActionListener(new ActionListener()
1146 public void actionPerformed(ActionEvent e)
1148 af.avc.sortAlignmentByFeatureScore(null);
1151 sortByDens.setFont(JvSwingUtils.getLabelFont());
1153 MessageManager.getString("label.sequence_sort_by_density"));
1154 sortByDens.addActionListener(new ActionListener()
1157 public void actionPerformed(ActionEvent e)
1159 af.avc.sortAlignmentByFeatureDensity(null);
1162 help.setFont(JvSwingUtils.getLabelFont());
1163 help.setText(MessageManager.getString("action.help"));
1164 help.addActionListener(new ActionListener()
1167 public void actionPerformed(ActionEvent e)
1171 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1172 } catch (HelpSetException e1)
1174 e1.printStackTrace();
1178 help.setFont(JvSwingUtils.getLabelFont());
1179 help.setText(MessageManager.getString("action.help"));
1180 help.addActionListener(new ActionListener()
1183 public void actionPerformed(ActionEvent e)
1187 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1188 } catch (HelpSetException e1)
1190 e1.printStackTrace();
1194 cancel.setFont(JvSwingUtils.getLabelFont());
1195 cancel.setText(MessageManager.getString("action.cancel"));
1196 cancel.addActionListener(new ActionListener()
1199 public void actionPerformed(ActionEvent e)
1201 fr.setTransparency(originalTransparency);
1202 updateFeatureRenderer(originalData);
1206 ok.setFont(JvSwingUtils.getLabelFont());
1207 ok.setText(MessageManager.getString("action.ok"));
1208 ok.addActionListener(new ActionListener()
1211 public void actionPerformed(ActionEvent e)
1216 loadColours.setFont(JvSwingUtils.getLabelFont());
1217 loadColours.setText(MessageManager.getString("label.load_colours"));
1218 loadColours.addActionListener(new ActionListener()
1221 public void actionPerformed(ActionEvent e)
1226 saveColours.setFont(JvSwingUtils.getLabelFont());
1227 saveColours.setText(MessageManager.getString("label.save_colours"));
1228 saveColours.addActionListener(new ActionListener()
1231 public void actionPerformed(ActionEvent e)
1236 transparency.addChangeListener(new ChangeListener()
1239 public void stateChanged(ChangeEvent evt)
1241 if (!inConstruction)
1243 fr.setTransparency((100 - transparency.getValue()) / 100f);
1244 af.alignPanel.paintAlignment(true,true);
1249 transparency.setMaximum(70);
1250 transparency.setToolTipText(
1251 MessageManager.getString("label.transparency_tip"));
1252 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1253 fetchDAS.addActionListener(new ActionListener()
1256 public void actionPerformed(ActionEvent e)
1258 fetchDAS_actionPerformed(e);
1261 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1262 saveDAS.addActionListener(new ActionListener()
1265 public void actionPerformed(ActionEvent e)
1267 saveDAS_actionPerformed(e);
1270 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1271 dasSettingsPane.setBorder(null);
1272 cancelDAS.setEnabled(false);
1273 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1274 cancelDAS.addActionListener(new ActionListener()
1277 public void actionPerformed(ActionEvent e)
1279 cancelDAS_actionPerformed(e);
1282 this.add(tabbedPane, java.awt.BorderLayout.CENTER);
1283 tabbedPane.addTab(MessageManager.getString("label.feature_settings"),
1285 tabbedPane.addTab(MessageManager.getString("label.das_settings"),
1287 bigPanel.add(transPanel, java.awt.BorderLayout.SOUTH);
1288 transbuttons.add(optimizeOrder);
1289 transbuttons.add(invert);
1290 transbuttons.add(sortByScore);
1291 transbuttons.add(sortByDens);
1292 transbuttons.add(help);
1293 JPanel sliderPanel = new JPanel();
1294 sliderPanel.add(transparency);
1295 transPanel.add(transparency);
1296 transPanel.add(transbuttons);
1297 buttonPanel.add(ok);
1298 buttonPanel.add(cancel);
1299 buttonPanel.add(loadColours);
1300 buttonPanel.add(saveColours);
1301 bigPanel.add(scrollPane, java.awt.BorderLayout.CENTER);
1302 dasSettingsPane.add(dasButtonPanel, java.awt.BorderLayout.SOUTH);
1303 dasButtonPanel.add(fetchDAS);
1304 dasButtonPanel.add(cancelDAS);
1305 dasButtonPanel.add(saveDAS);
1306 settingsPane.add(bigPanel, java.awt.BorderLayout.CENTER);
1307 settingsPane.add(buttonPanel, java.awt.BorderLayout.SOUTH);
1310 public void fetchDAS_actionPerformed(ActionEvent e)
1312 fetchDAS.setEnabled(false);
1313 cancelDAS.setEnabled(true);
1314 dassourceBrowser.setGuiEnabled(false);
1315 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1316 .getSelectedSources();
1317 doDasFeatureFetch(selectedSources, true, true);
1321 * get the features from selectedSources for all or the current selection
1323 * @param selectedSources
1324 * @param checkDbRefs
1325 * @param promptFetchDbRefs
1327 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1328 boolean checkDbRefs, boolean promptFetchDbRefs)
1330 SequenceI[] dataset, seqs;
1332 AlignmentViewport vp = af.getViewport();
1333 if (vp.getSelectionGroup() != null
1334 && vp.getSelectionGroup().getSize() > 0)
1336 iSize = vp.getSelectionGroup().getSize();
1337 dataset = new SequenceI[iSize];
1338 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1342 iSize = vp.getAlignment().getHeight();
1343 seqs = vp.getAlignment().getSequencesArray();
1346 dataset = new SequenceI[iSize];
1347 for (int i = 0; i < iSize; i++)
1349 dataset[i] = seqs[i].getDatasetSequence();
1352 cancelDAS.setEnabled(true);
1353 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1354 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1355 af.getViewport().setShowSequenceFeatures(true);
1356 af.showSeqFeatures.setSelected(true);
1360 * blocking call to initialise the das source browser
1362 public void initDasSources()
1364 dassourceBrowser.initDasSources();
1368 * examine the current list of das sources and return any matching the given
1369 * nicknames in sources
1372 * Vector of Strings to resolve to DAS source nicknames.
1373 * @return sources that are present in source list.
1375 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1377 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1381 * get currently selected das sources. ensure you have called initDasSources
1382 * before calling this.
1384 * @return vector of selected das source nicknames
1386 public Vector<jalviewSourceI> getSelectedSources()
1388 return dassourceBrowser.getSelectedSources();
1392 * properly initialise DAS fetcher and then initiate a new thread to fetch
1393 * features from the named sources (rather than any turned on by default)
1397 * if true then runs in same thread, otherwise passes to the Swing
1400 public void fetchDasFeatures(Vector<String> sources, boolean block)
1403 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1404 .resolveSourceNicknames(sources);
1405 if (resolved.size() == 0)
1407 resolved = dassourceBrowser.getSelectedSources();
1409 if (resolved.size() > 0)
1411 final List<jalviewSourceI> dassources = resolved;
1412 fetchDAS.setEnabled(false);
1413 // cancelDAS.setEnabled(true); doDasFetch does this.
1414 Runnable fetcher = new Runnable()
1420 doDasFeatureFetch(dassources, true, false);
1430 SwingUtilities.invokeLater(fetcher);
1435 public void saveDAS_actionPerformed(ActionEvent e)
1438 .saveProperties(jalview.bin.Cache.applicationProperties);
1441 public void complete()
1443 fetchDAS.setEnabled(true);
1444 cancelDAS.setEnabled(false);
1445 dassourceBrowser.setGuiEnabled(true);
1449 public void cancelDAS_actionPerformed(ActionEvent e)
1451 if (dasFeatureFetcher != null)
1453 dasFeatureFetcher.cancel();
1458 public void noDasSourceActive()
1461 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1462 MessageManager.getString("label.no_das_sources_selected_warn"),
1463 MessageManager.getString("label.no_das_sources_selected_title"),
1464 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1467 // ///////////////////////////////////////////////////////////////////////
1468 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1469 // ///////////////////////////////////////////////////////////////////////
1470 class FeatureTableModel extends AbstractTableModel
1472 FeatureTableModel(Object[][] data)
1477 private String[] columnNames = {
1478 MessageManager.getString("label.feature_type"),
1479 MessageManager.getString("action.colour"),
1480 MessageManager.getString("label.display") };
1482 private Object[][] data;
1484 public Object[][] getData()
1489 public void setData(Object[][] data)
1495 public int getColumnCount()
1497 return columnNames.length;
1500 public Object[] getRow(int row)
1506 public int getRowCount()
1512 public String getColumnName(int col)
1514 return columnNames[col];
1518 public Object getValueAt(int row, int col)
1520 return data[row][col];
1524 public Class getColumnClass(int c)
1526 return getValueAt(0, c).getClass();
1530 public boolean isCellEditable(int row, int col)
1532 return col == 0 ? false : true;
1536 public void setValueAt(Object value, int row, int col)
1538 data[row][col] = value;
1539 fireTableCellUpdated(row, col);
1540 updateFeatureRenderer(data);
1545 class ColorRenderer extends JLabel implements TableCellRenderer
1547 javax.swing.border.Border unselectedBorder = null;
1549 javax.swing.border.Border selectedBorder = null;
1551 final String baseTT = "Click to edit, right/apple click for menu.";
1553 public ColorRenderer()
1555 setOpaque(true); // MUST do this for background to show up.
1556 setHorizontalTextPosition(SwingConstants.CENTER);
1557 setVerticalTextPosition(SwingConstants.CENTER);
1561 public Component getTableCellRendererComponent(JTable tbl, Object color,
1562 boolean isSelected, boolean hasFocus, int row, int column)
1564 FeatureColourI cellColour = (FeatureColourI) color;
1565 // JLabel comp = new JLabel();
1569 // setBounds(getBounds());
1571 setToolTipText(baseTT);
1572 setBackground(tbl.getBackground());
1573 if (!cellColour.isSimpleColour())
1575 Rectangle cr = tbl.getCellRect(row, column, false);
1576 FeatureSettings.renderGraduatedColor(this, cellColour,
1577 (int) cr.getWidth(), (int) cr.getHeight());
1584 newColor = cellColour.getColour();
1585 setBackground(newColor);
1589 if (selectedBorder == null)
1591 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1592 tbl.getSelectionBackground());
1594 setBorder(selectedBorder);
1598 if (unselectedBorder == null)
1600 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1601 tbl.getBackground());
1603 setBorder(unselectedBorder);
1611 * update comp using rendering settings from gcol
1616 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1618 int w = comp.getWidth(), h = comp.getHeight();
1621 w = (int) comp.getPreferredSize().getWidth();
1622 h = (int) comp.getPreferredSize().getHeight();
1629 renderGraduatedColor(comp, gcol, w, h);
1632 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1635 boolean thr = false;
1638 if (gcol.isAboveThreshold())
1642 tt += "Thresholded (Above " + gcol.getThreshold() + ") ";
1644 if (gcol.isBelowThreshold())
1648 tt += "Thresholded (Below " + gcol.getThreshold() + ") ";
1650 if (gcol.isColourByLabel())
1652 tt = "Coloured by label text. " + tt;
1662 Color newColor = gcol.getMaxColour();
1663 comp.setBackground(newColor);
1664 // System.err.println("Width is " + w / 2);
1665 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1666 comp.setIcon(ficon);
1667 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1668 // + newColor.getGreen() + ", " + newColor.getBlue()
1669 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1670 // + ", " + minCol.getBlue() + ")");
1672 comp.setHorizontalAlignment(SwingConstants.CENTER);
1674 if (tt.length() > 0)
1676 if (comp.getToolTipText() == null)
1678 comp.setToolTipText(tt);
1682 comp.setToolTipText(tt + " " + comp.getToolTipText());
1688 class FeatureIcon implements Icon
1690 FeatureColourI gcol;
1694 boolean midspace = false;
1696 int width = 50, height = 20;
1698 int s1, e1; // start and end of midpoint band for thresholded symbol
1700 Color mpcolour = Color.white;
1702 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1722 public int getIconWidth()
1728 public int getIconHeight()
1734 public void paintIcon(Component c, Graphics g, int x, int y)
1737 if (gcol.isColourByLabel())
1740 g.fillRect(0, 0, width, height);
1741 // need an icon here.
1742 g.setColor(gcol.getMaxColour());
1744 g.setFont(new Font("Verdana", Font.PLAIN, 9));
1746 // g.setFont(g.getFont().deriveFont(
1747 // AffineTransform.getScaleInstance(
1748 // width/g.getFontMetrics().stringWidth("Label"),
1749 // height/g.getFontMetrics().getHeight())));
1751 g.drawString(MessageManager.getString("label.label"), 0, 0);
1756 Color minCol = gcol.getMinColour();
1758 g.fillRect(0, 0, s1, height);
1761 g.setColor(Color.white);
1762 g.fillRect(s1, 0, e1 - s1, height);
1764 g.setColor(gcol.getMaxColour());
1765 g.fillRect(0, e1, width - e1, height);
1770 class ColorEditor extends AbstractCellEditor
1771 implements TableCellEditor, ActionListener
1775 FeatureColourI currentColor;
1777 FeatureColourChooser chooser;
1783 JColorChooser colorChooser;
1787 protected static final String EDIT = "edit";
1789 int selectedRow = 0;
1791 public ColorEditor(FeatureSettings me)
1794 // Set up the editor (from the table's point of view),
1795 // which is a button.
1796 // This button brings up the color chooser dialog,
1797 // which is the editor from the user's point of view.
1798 button = new JButton();
1799 button.setActionCommand(EDIT);
1800 button.addActionListener(this);
1801 button.setBorderPainted(false);
1802 // Set up the dialog that the button brings up.
1803 colorChooser = new JColorChooser();
1804 dialog = JColorChooser.createDialog(button, "Select new Colour", true, // modal
1805 colorChooser, this, // OK button handler
1806 null); // no CANCEL button handler
1810 * Handles events from the editor button and from the dialog's OK button.
1813 public void actionPerformed(ActionEvent e)
1816 if (EDIT.equals(e.getActionCommand()))
1818 // The user has clicked the cell, so
1819 // bring up the dialog.
1820 if (currentColor.isSimpleColour())
1822 // bring up simple color chooser
1823 button.setBackground(currentColor.getColour());
1824 colorChooser.setColor(currentColor.getColour());
1825 dialog.setVisible(true);
1829 // bring up graduated chooser.
1830 chooser = new FeatureColourChooser(me.fr, type);
1831 chooser.setRequestFocusEnabled(true);
1832 chooser.requestFocus();
1833 chooser.addActionListener(this);
1835 // Make the renderer reappear.
1836 fireEditingStopped();
1840 { // User pressed dialog's "OK" button.
1841 if (currentColor.isSimpleColour())
1843 currentColor = new FeatureColour(colorChooser.getColor());
1847 currentColor = chooser.getLastColour();
1849 me.table.setValueAt(getCellEditorValue(), selectedRow, 1);
1850 fireEditingStopped();
1851 me.table.validate();
1855 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1857 public Object getCellEditorValue()
1859 return currentColor;
1862 // Implement the one method defined by TableCellEditor.
1864 public Component getTableCellEditorComponent(JTable table, Object value,
1865 boolean isSelected, int row, int column)
1867 currentColor = (FeatureColourI) value;
1868 this.selectedRow = row;
1869 type = me.table.getValueAt(row, 0).toString();
1870 button.setOpaque(true);
1871 button.setBackground(me.getBackground());
1872 if (!currentColor.isSimpleColour())
1874 JLabel btn = new JLabel();
1875 btn.setSize(button.getSize());
1876 FeatureSettings.renderGraduatedColor(btn, currentColor);
1877 button.setBackground(btn.getBackground());
1878 button.setIcon(btn.getIcon());
1879 button.setText(btn.getText());
1884 button.setIcon(null);
1885 button.setBackground(currentColor.getColour());