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 af.alignPanel.getSeqPanel().seqCanvas.repaint();
545 if (af.alignPanel.overviewPanel != null)
547 af.alignPanel.overviewPanel.updateOverviewImage();
550 resetTable(new String[] { grp });
553 groupPanel.add(check);
557 boolean resettingTable = false;
559 synchronized void resetTable(String[] groupChanged)
565 resettingTable = true;
566 typeWidth = new Hashtable<>();
567 // TODO: change avWidth calculation to 'per-sequence' average and use long
570 Set<String> displayableTypes = new HashSet<>();
571 Set<String> foundGroups = new HashSet<>();
574 * determine which feature types may be visible depending on
575 * which groups are selected, and recompute average width data
577 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
580 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
583 * get the sequence's groups for positional features
584 * and keep track of which groups are visible
586 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
587 Set<String> visibleGroups = new HashSet<>();
588 for (String group : groups)
590 if (group == null || checkGroupState(group))
592 visibleGroups.add(group);
595 foundGroups.addAll(groups);
598 * get distinct feature types for visible groups
599 * record distinct visible types, and their count and total length
601 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
602 visibleGroups.toArray(new String[visibleGroups.size()]));
603 for (String type : types)
605 displayableTypes.add(type);
606 float[] avWidth = typeWidth.get(type);
609 avWidth = new float[2];
610 typeWidth.put(type, avWidth);
612 // todo this could include features with a non-visible group
613 // - do we greatly care?
614 // todo should we include non-displayable features here, and only
615 // update when features are added?
616 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
617 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
621 Object[][] data = new Object[displayableTypes.size()][3];
624 if (fr.hasRenderOrder())
628 fr.findAllFeatures(groupChanged != null); // prod to update
629 // colourschemes. but don't
631 // First add the checks in the previous render order,
632 // in case the window has been closed and reopened
634 List<String> frl = fr.getRenderOrder();
635 for (int ro = frl.size() - 1; ro > -1; ro--)
637 String type = frl.get(ro);
639 if (!displayableTypes.contains(type))
644 data[dataIndex][0] = type;
645 data[dataIndex][1] = fr.getFeatureStyle(type);
646 data[dataIndex][2] = new Boolean(
647 af.getViewport().getFeaturesDisplayed().isVisible(type));
649 displayableTypes.remove(type);
654 * process any extra features belonging only to
655 * a group which was just selected
657 while (!displayableTypes.isEmpty())
659 String type = displayableTypes.iterator().next();
660 data[dataIndex][0] = type;
662 data[dataIndex][1] = fr.getFeatureStyle(type);
663 if (data[dataIndex][1] == null)
665 // "Colour has been updated in another view!!"
666 fr.clearRenderOrder();
670 data[dataIndex][2] = new Boolean(true);
672 displayableTypes.remove(type);
675 if (originalData == null)
677 originalData = new Object[data.length][3];
678 for (int i = 0; i < data.length; i++)
680 System.arraycopy(data[i], 0, originalData[i], 0, 3);
685 updateOriginalData(data);
688 table.setModel(new FeatureTableModel(data));
689 table.getColumnModel().getColumn(0).setPreferredWidth(200);
691 groupPanel.setLayout(
692 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
693 pruneGroups(foundGroups);
694 groupPanel.validate();
696 updateFeatureRenderer(data, groupChanged != null);
697 resettingTable = false;
701 * Updates 'originalData' (used for restore on Cancel) if we detect that
702 * changes have been made outwith this dialog
704 * <li>a new feature type added (and made visible)</li>
705 * <li>a feature colour changed (in the Amend Features dialog)</li>
710 protected void updateOriginalData(Object[][] foundData)
712 // todo LinkedHashMap instead of Object[][] would be nice
714 Object[][] currentData = ((FeatureTableModel) table.getModel())
716 for (Object[] row : foundData)
718 String type = (String) row[0];
719 boolean found = false;
720 for (Object[] current : currentData)
722 if (type.equals(current[0]))
726 * currently dependent on object equality here;
727 * really need an equals method on FeatureColour
729 if (!row[1].equals(current[1]))
732 * feature colour has changed externally - update originalData
734 for (Object[] original : originalData)
736 if (type.equals(original[0]))
738 original[1] = row[1];
749 * new feature detected - add to original data (on top)
751 Object[][] newData = new Object[originalData.length + 1][3];
752 for (int i = 0; i < originalData.length; i++)
754 System.arraycopy(originalData[i], 0, newData[i + 1], 0, 3);
757 originalData = newData;
763 * Remove from the groups panel any checkboxes for groups that are not in the
764 * foundGroups set. This enables removing a group from the display when the
765 * last feature in that group is deleted.
769 protected void pruneGroups(Set<String> foundGroups)
771 for (int g = 0; g < groupPanel.getComponentCount(); g++)
773 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
774 if (!foundGroups.contains(checkbox.getText()))
776 groupPanel.remove(checkbox);
782 * reorder data based on the featureRenderers global priority list.
786 private void ensureOrder(Object[][] data)
788 boolean sort = false;
789 float[] order = new float[data.length];
790 for (int i = 0; i < order.length; i++)
792 order[i] = fr.getOrder(data[i][0].toString());
795 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
799 sort = sort || order[i - 1] > order[i];
804 jalview.util.QuickSort.sort(order, data);
810 JalviewFileChooser chooser = new JalviewFileChooser("fc",
811 "Sequence Feature Colours");
812 chooser.setFileView(new JalviewFileView());
813 chooser.setDialogTitle(
814 MessageManager.getString("label.load_feature_colours"));
815 chooser.setToolTipText(MessageManager.getString("action.load"));
817 int value = chooser.showOpenDialog(this);
819 if (value == JalviewFileChooser.APPROVE_OPTION)
821 File file = chooser.getSelectedFile();
825 InputStreamReader in = new InputStreamReader(
826 new FileInputStream(file), "UTF-8");
828 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
830 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
833 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
836 Color mincol = null, maxcol = null;
839 mincol = new Color(Integer.parseInt(newcol.getMinRGB(), 16));
840 maxcol = new Color(Integer.parseInt(newcol.getRGB(), 16));
842 } catch (Exception e)
844 Cache.log.warn("Couldn't parse out graduated feature color.",
847 FeatureColourI gcol = new FeatureColour(mincol, maxcol,
848 newcol.getMin(), newcol.getMax());
849 if (newcol.hasAutoScale())
851 gcol.setAutoScaled(newcol.getAutoScale());
853 if (newcol.hasColourByLabel())
855 gcol.setColourByLabel(newcol.getColourByLabel());
857 if (newcol.hasThreshold())
859 gcol.setThreshold(newcol.getThreshold());
861 if (newcol.getThreshType().length() > 0)
863 String ttyp = newcol.getThreshType();
864 if (ttyp.equalsIgnoreCase("ABOVE"))
866 gcol.setAboveThreshold(true);
868 if (ttyp.equalsIgnoreCase("BELOW"))
870 gcol.setBelowThreshold(true);
873 fr.setColour(name = newcol.getName(), gcol);
877 Color color = new Color(
878 Integer.parseInt(jucs.getColour(i).getRGB(), 16));
879 fr.setColour(name = jucs.getColour(i).getName(),
880 new FeatureColour(color));
882 fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
887 Object[][] data = ((FeatureTableModel) table.getModel())
890 updateFeatureRenderer(data, false);
893 } catch (Exception ex)
895 System.out.println("Error loading User Colour File\n" + ex);
902 JalviewFileChooser chooser = new JalviewFileChooser("fc",
903 "Sequence Feature Colours");
904 chooser.setFileView(new JalviewFileView());
905 chooser.setDialogTitle(
906 MessageManager.getString("label.save_feature_colours"));
907 chooser.setToolTipText(MessageManager.getString("action.save"));
909 int value = chooser.showSaveDialog(this);
911 if (value == JalviewFileChooser.APPROVE_OPTION)
913 String choice = chooser.getSelectedFile().getPath();
914 jalview.schemabinding.version2.JalviewUserColours ucs = new jalview.schemabinding.version2.JalviewUserColours();
915 ucs.setSchemeName("Sequence Features");
918 PrintWriter out = new PrintWriter(new OutputStreamWriter(
919 new FileOutputStream(choice), "UTF-8"));
921 Set<String> fr_colours = fr.getAllFeatureColours();
922 Iterator<String> e = fr_colours.iterator();
923 float[] sortOrder = new float[fr_colours.size()];
924 String[] sortTypes = new String[fr_colours.size()];
928 sortTypes[i] = e.next();
929 sortOrder[i] = fr.getOrder(sortTypes[i]);
932 QuickSort.sort(sortOrder, sortTypes);
934 for (i = 0; i < sortTypes.length; i++)
936 jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
937 col.setName(sortTypes[i]);
938 FeatureColourI fcol = fr.getFeatureStyle(sortTypes[i]);
939 if (fcol.isSimpleColour())
941 col.setRGB(Format.getHexString(fcol.getColour()));
945 col.setRGB(Format.getHexString(fcol.getMaxColour()));
946 col.setMin(fcol.getMin());
947 col.setMax(fcol.getMax());
949 jalview.util.Format.getHexString(fcol.getMinColour()));
950 col.setAutoScale(fcol.isAutoScaled());
951 col.setThreshold(fcol.getThreshold());
952 col.setColourByLabel(fcol.isColourByLabel());
953 col.setThreshType(fcol.isAboveThreshold() ? "ABOVE"
954 : (fcol.isBelowThreshold() ? "BELOW" : "NONE"));
960 } catch (Exception ex)
962 ex.printStackTrace();
967 public void invertSelection()
969 for (int i = 0; i < table.getRowCount(); i++)
971 Boolean value = (Boolean) table.getValueAt(i, 2);
973 table.setValueAt(new Boolean(!value.booleanValue()), i, 2);
977 public void orderByAvWidth()
979 if (table == null || table.getModel() == null)
983 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
984 float[] width = new float[data.length];
988 for (int i = 0; i < data.length; i++)
990 awidth = typeWidth.get(data[i][0]);
993 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
994 // weight - but have to make per
995 // sequence, too (awidth[2])
996 // if (width[i]==1) // hack to distinguish single width sequences.
1008 boolean sort = false;
1009 for (int i = 0; i < width.length; i++)
1011 // awidth = (float[]) typeWidth.get(data[i][0]);
1014 width[i] = fr.getOrder(data[i][0].toString());
1017 width[i] = fr.setOrder(data[i][0].toString(), i / data.length);
1022 width[i] /= max; // normalize
1023 fr.setOrder(data[i][0].toString(), width[i]); // store for later
1027 sort = sort || width[i - 1] > width[i];
1032 jalview.util.QuickSort.sort(width, data);
1033 // update global priority order
1036 updateFeatureRenderer(data, false);
1044 frame.setClosed(true);
1045 } catch (Exception exe)
1051 public void updateFeatureRenderer(Object[][] data)
1053 updateFeatureRenderer(data, true);
1057 * Update the priority order of features; only repaint if this changed the
1058 * order of visible features
1063 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1065 if (fr.setFeaturePriority(data, visibleNew))
1067 af.alignPanel.paintAlignment(true, true);
1071 int selectedRow = -1;
1073 JTabbedPane tabbedPane = new JTabbedPane();
1075 BorderLayout borderLayout1 = new BorderLayout();
1077 BorderLayout borderLayout2 = new BorderLayout();
1079 BorderLayout borderLayout3 = new BorderLayout();
1081 JPanel bigPanel = new JPanel();
1083 BorderLayout borderLayout4 = new BorderLayout();
1085 JButton invert = new JButton();
1087 JPanel buttonPanel = new JPanel();
1089 JButton cancel = new JButton();
1091 JButton ok = new JButton();
1093 JButton loadColours = new JButton();
1095 JButton saveColours = new JButton();
1097 JPanel dasButtonPanel = new JPanel();
1099 JButton fetchDAS = new JButton();
1101 JButton saveDAS = new JButton();
1103 JButton cancelDAS = new JButton();
1105 JButton optimizeOrder = new JButton();
1107 JButton sortByScore = new JButton();
1109 JButton sortByDens = new JButton();
1111 JButton help = new JButton();
1113 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1115 private void jbInit() throws Exception
1117 this.setLayout(borderLayout1);
1118 settingsPane.setLayout(borderLayout2);
1119 dasSettingsPane.setLayout(borderLayout3);
1120 bigPanel.setLayout(borderLayout4);
1122 groupPanel = new JPanel();
1123 bigPanel.add(groupPanel, BorderLayout.NORTH);
1125 invert.setFont(JvSwingUtils.getLabelFont());
1126 invert.setText(MessageManager.getString("label.invert_selection"));
1127 invert.addActionListener(new ActionListener()
1130 public void actionPerformed(ActionEvent e)
1135 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1136 optimizeOrder.setText(MessageManager.getString("label.optimise_order"));
1137 optimizeOrder.addActionListener(new ActionListener()
1140 public void actionPerformed(ActionEvent e)
1145 sortByScore.setFont(JvSwingUtils.getLabelFont());
1147 .setText(MessageManager.getString("label.seq_sort_by_score"));
1148 sortByScore.addActionListener(new ActionListener()
1151 public void actionPerformed(ActionEvent e)
1153 af.avc.sortAlignmentByFeatureScore(null);
1156 sortByDens.setFont(JvSwingUtils.getLabelFont());
1158 MessageManager.getString("label.sequence_sort_by_density"));
1159 sortByDens.addActionListener(new ActionListener()
1162 public void actionPerformed(ActionEvent e)
1164 af.avc.sortAlignmentByFeatureDensity(null);
1167 help.setFont(JvSwingUtils.getLabelFont());
1168 help.setText(MessageManager.getString("action.help"));
1169 help.addActionListener(new ActionListener()
1172 public void actionPerformed(ActionEvent e)
1176 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1177 } catch (HelpSetException e1)
1179 e1.printStackTrace();
1183 help.setFont(JvSwingUtils.getLabelFont());
1184 help.setText(MessageManager.getString("action.help"));
1185 help.addActionListener(new ActionListener()
1188 public void actionPerformed(ActionEvent e)
1192 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1193 } catch (HelpSetException e1)
1195 e1.printStackTrace();
1199 cancel.setFont(JvSwingUtils.getLabelFont());
1200 cancel.setText(MessageManager.getString("action.cancel"));
1201 cancel.addActionListener(new ActionListener()
1204 public void actionPerformed(ActionEvent e)
1206 fr.setTransparency(originalTransparency);
1207 updateFeatureRenderer(originalData);
1211 ok.setFont(JvSwingUtils.getLabelFont());
1212 ok.setText(MessageManager.getString("action.ok"));
1213 ok.addActionListener(new ActionListener()
1216 public void actionPerformed(ActionEvent e)
1221 loadColours.setFont(JvSwingUtils.getLabelFont());
1222 loadColours.setText(MessageManager.getString("label.load_colours"));
1223 loadColours.addActionListener(new ActionListener()
1226 public void actionPerformed(ActionEvent e)
1231 saveColours.setFont(JvSwingUtils.getLabelFont());
1232 saveColours.setText(MessageManager.getString("label.save_colours"));
1233 saveColours.addActionListener(new ActionListener()
1236 public void actionPerformed(ActionEvent e)
1241 transparency.addChangeListener(new ChangeListener()
1244 public void stateChanged(ChangeEvent evt)
1246 if (!inConstruction)
1248 fr.setTransparency((100 - transparency.getValue()) / 100f);
1249 af.alignPanel.paintAlignment(true,true);
1254 transparency.setMaximum(70);
1255 transparency.setToolTipText(
1256 MessageManager.getString("label.transparency_tip"));
1257 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1258 fetchDAS.addActionListener(new ActionListener()
1261 public void actionPerformed(ActionEvent e)
1263 fetchDAS_actionPerformed(e);
1266 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1267 saveDAS.addActionListener(new ActionListener()
1270 public void actionPerformed(ActionEvent e)
1272 saveDAS_actionPerformed(e);
1275 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1276 dasSettingsPane.setBorder(null);
1277 cancelDAS.setEnabled(false);
1278 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1279 cancelDAS.addActionListener(new ActionListener()
1282 public void actionPerformed(ActionEvent e)
1284 cancelDAS_actionPerformed(e);
1287 this.add(tabbedPane, java.awt.BorderLayout.CENTER);
1288 tabbedPane.addTab(MessageManager.getString("label.feature_settings"),
1290 tabbedPane.addTab(MessageManager.getString("label.das_settings"),
1292 bigPanel.add(transPanel, java.awt.BorderLayout.SOUTH);
1293 transbuttons.add(optimizeOrder);
1294 transbuttons.add(invert);
1295 transbuttons.add(sortByScore);
1296 transbuttons.add(sortByDens);
1297 transbuttons.add(help);
1298 JPanel sliderPanel = new JPanel();
1299 sliderPanel.add(transparency);
1300 transPanel.add(transparency);
1301 transPanel.add(transbuttons);
1302 buttonPanel.add(ok);
1303 buttonPanel.add(cancel);
1304 buttonPanel.add(loadColours);
1305 buttonPanel.add(saveColours);
1306 bigPanel.add(scrollPane, java.awt.BorderLayout.CENTER);
1307 dasSettingsPane.add(dasButtonPanel, java.awt.BorderLayout.SOUTH);
1308 dasButtonPanel.add(fetchDAS);
1309 dasButtonPanel.add(cancelDAS);
1310 dasButtonPanel.add(saveDAS);
1311 settingsPane.add(bigPanel, java.awt.BorderLayout.CENTER);
1312 settingsPane.add(buttonPanel, java.awt.BorderLayout.SOUTH);
1315 public void fetchDAS_actionPerformed(ActionEvent e)
1317 fetchDAS.setEnabled(false);
1318 cancelDAS.setEnabled(true);
1319 dassourceBrowser.setGuiEnabled(false);
1320 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1321 .getSelectedSources();
1322 doDasFeatureFetch(selectedSources, true, true);
1326 * get the features from selectedSources for all or the current selection
1328 * @param selectedSources
1329 * @param checkDbRefs
1330 * @param promptFetchDbRefs
1332 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1333 boolean checkDbRefs, boolean promptFetchDbRefs)
1335 SequenceI[] dataset, seqs;
1337 AlignmentViewport vp = af.getViewport();
1338 if (vp.getSelectionGroup() != null
1339 && vp.getSelectionGroup().getSize() > 0)
1341 iSize = vp.getSelectionGroup().getSize();
1342 dataset = new SequenceI[iSize];
1343 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1347 iSize = vp.getAlignment().getHeight();
1348 seqs = vp.getAlignment().getSequencesArray();
1351 dataset = new SequenceI[iSize];
1352 for (int i = 0; i < iSize; i++)
1354 dataset[i] = seqs[i].getDatasetSequence();
1357 cancelDAS.setEnabled(true);
1358 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1359 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1360 af.getViewport().setShowSequenceFeatures(true);
1361 af.showSeqFeatures.setSelected(true);
1365 * blocking call to initialise the das source browser
1367 public void initDasSources()
1369 dassourceBrowser.initDasSources();
1373 * examine the current list of das sources and return any matching the given
1374 * nicknames in sources
1377 * Vector of Strings to resolve to DAS source nicknames.
1378 * @return sources that are present in source list.
1380 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1382 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1386 * get currently selected das sources. ensure you have called initDasSources
1387 * before calling this.
1389 * @return vector of selected das source nicknames
1391 public Vector<jalviewSourceI> getSelectedSources()
1393 return dassourceBrowser.getSelectedSources();
1397 * properly initialise DAS fetcher and then initiate a new thread to fetch
1398 * features from the named sources (rather than any turned on by default)
1402 * if true then runs in same thread, otherwise passes to the Swing
1405 public void fetchDasFeatures(Vector<String> sources, boolean block)
1408 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1409 .resolveSourceNicknames(sources);
1410 if (resolved.size() == 0)
1412 resolved = dassourceBrowser.getSelectedSources();
1414 if (resolved.size() > 0)
1416 final List<jalviewSourceI> dassources = resolved;
1417 fetchDAS.setEnabled(false);
1418 // cancelDAS.setEnabled(true); doDasFetch does this.
1419 Runnable fetcher = new Runnable()
1425 doDasFeatureFetch(dassources, true, false);
1435 SwingUtilities.invokeLater(fetcher);
1440 public void saveDAS_actionPerformed(ActionEvent e)
1443 .saveProperties(jalview.bin.Cache.applicationProperties);
1446 public void complete()
1448 fetchDAS.setEnabled(true);
1449 cancelDAS.setEnabled(false);
1450 dassourceBrowser.setGuiEnabled(true);
1454 public void cancelDAS_actionPerformed(ActionEvent e)
1456 if (dasFeatureFetcher != null)
1458 dasFeatureFetcher.cancel();
1463 public void noDasSourceActive()
1466 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1467 MessageManager.getString("label.no_das_sources_selected_warn"),
1468 MessageManager.getString("label.no_das_sources_selected_title"),
1469 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1472 // ///////////////////////////////////////////////////////////////////////
1473 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1474 // ///////////////////////////////////////////////////////////////////////
1475 class FeatureTableModel extends AbstractTableModel
1477 FeatureTableModel(Object[][] data)
1482 private String[] columnNames = {
1483 MessageManager.getString("label.feature_type"),
1484 MessageManager.getString("action.colour"),
1485 MessageManager.getString("label.display") };
1487 private Object[][] data;
1489 public Object[][] getData()
1494 public void setData(Object[][] data)
1500 public int getColumnCount()
1502 return columnNames.length;
1505 public Object[] getRow(int row)
1511 public int getRowCount()
1517 public String getColumnName(int col)
1519 return columnNames[col];
1523 public Object getValueAt(int row, int col)
1525 return data[row][col];
1529 public Class getColumnClass(int c)
1531 return getValueAt(0, c).getClass();
1535 public boolean isCellEditable(int row, int col)
1537 return col == 0 ? false : true;
1541 public void setValueAt(Object value, int row, int col)
1543 data[row][col] = value;
1544 fireTableCellUpdated(row, col);
1545 updateFeatureRenderer(data);
1550 class ColorRenderer extends JLabel implements TableCellRenderer
1552 javax.swing.border.Border unselectedBorder = null;
1554 javax.swing.border.Border selectedBorder = null;
1556 final String baseTT = "Click to edit, right/apple click for menu.";
1558 public ColorRenderer()
1560 setOpaque(true); // MUST do this for background to show up.
1561 setHorizontalTextPosition(SwingConstants.CENTER);
1562 setVerticalTextPosition(SwingConstants.CENTER);
1566 public Component getTableCellRendererComponent(JTable tbl, Object color,
1567 boolean isSelected, boolean hasFocus, int row, int column)
1569 FeatureColourI cellColour = (FeatureColourI) color;
1570 // JLabel comp = new JLabel();
1574 // setBounds(getBounds());
1576 setToolTipText(baseTT);
1577 setBackground(tbl.getBackground());
1578 if (!cellColour.isSimpleColour())
1580 Rectangle cr = tbl.getCellRect(row, column, false);
1581 FeatureSettings.renderGraduatedColor(this, cellColour,
1582 (int) cr.getWidth(), (int) cr.getHeight());
1589 newColor = cellColour.getColour();
1590 setBackground(newColor);
1594 if (selectedBorder == null)
1596 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1597 tbl.getSelectionBackground());
1599 setBorder(selectedBorder);
1603 if (unselectedBorder == null)
1605 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1606 tbl.getBackground());
1608 setBorder(unselectedBorder);
1616 * update comp using rendering settings from gcol
1621 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1623 int w = comp.getWidth(), h = comp.getHeight();
1626 w = (int) comp.getPreferredSize().getWidth();
1627 h = (int) comp.getPreferredSize().getHeight();
1634 renderGraduatedColor(comp, gcol, w, h);
1637 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1640 boolean thr = false;
1643 if (gcol.isAboveThreshold())
1647 tt += "Thresholded (Above " + gcol.getThreshold() + ") ";
1649 if (gcol.isBelowThreshold())
1653 tt += "Thresholded (Below " + gcol.getThreshold() + ") ";
1655 if (gcol.isColourByLabel())
1657 tt = "Coloured by label text. " + tt;
1667 Color newColor = gcol.getMaxColour();
1668 comp.setBackground(newColor);
1669 // System.err.println("Width is " + w / 2);
1670 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1671 comp.setIcon(ficon);
1672 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1673 // + newColor.getGreen() + ", " + newColor.getBlue()
1674 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1675 // + ", " + minCol.getBlue() + ")");
1677 comp.setHorizontalAlignment(SwingConstants.CENTER);
1679 if (tt.length() > 0)
1681 if (comp.getToolTipText() == null)
1683 comp.setToolTipText(tt);
1687 comp.setToolTipText(tt + " " + comp.getToolTipText());
1693 class FeatureIcon implements Icon
1695 FeatureColourI gcol;
1699 boolean midspace = false;
1701 int width = 50, height = 20;
1703 int s1, e1; // start and end of midpoint band for thresholded symbol
1705 Color mpcolour = Color.white;
1707 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1727 public int getIconWidth()
1733 public int getIconHeight()
1739 public void paintIcon(Component c, Graphics g, int x, int y)
1742 if (gcol.isColourByLabel())
1745 g.fillRect(0, 0, width, height);
1746 // need an icon here.
1747 g.setColor(gcol.getMaxColour());
1749 g.setFont(new Font("Verdana", Font.PLAIN, 9));
1751 // g.setFont(g.getFont().deriveFont(
1752 // AffineTransform.getScaleInstance(
1753 // width/g.getFontMetrics().stringWidth("Label"),
1754 // height/g.getFontMetrics().getHeight())));
1756 g.drawString(MessageManager.getString("label.label"), 0, 0);
1761 Color minCol = gcol.getMinColour();
1763 g.fillRect(0, 0, s1, height);
1766 g.setColor(Color.white);
1767 g.fillRect(s1, 0, e1 - s1, height);
1769 g.setColor(gcol.getMaxColour());
1770 g.fillRect(0, e1, width - e1, height);
1775 class ColorEditor extends AbstractCellEditor
1776 implements TableCellEditor, ActionListener
1780 FeatureColourI currentColor;
1782 FeatureColourChooser chooser;
1788 JColorChooser colorChooser;
1792 protected static final String EDIT = "edit";
1794 int selectedRow = 0;
1796 public ColorEditor(FeatureSettings me)
1799 // Set up the editor (from the table's point of view),
1800 // which is a button.
1801 // This button brings up the color chooser dialog,
1802 // which is the editor from the user's point of view.
1803 button = new JButton();
1804 button.setActionCommand(EDIT);
1805 button.addActionListener(this);
1806 button.setBorderPainted(false);
1807 // Set up the dialog that the button brings up.
1808 colorChooser = new JColorChooser();
1809 dialog = JColorChooser.createDialog(button, "Select new Colour", true, // modal
1810 colorChooser, this, // OK button handler
1811 null); // no CANCEL button handler
1815 * Handles events from the editor button and from the dialog's OK button.
1818 public void actionPerformed(ActionEvent e)
1821 if (EDIT.equals(e.getActionCommand()))
1823 // The user has clicked the cell, so
1824 // bring up the dialog.
1825 if (currentColor.isSimpleColour())
1827 // bring up simple color chooser
1828 button.setBackground(currentColor.getColour());
1829 colorChooser.setColor(currentColor.getColour());
1830 dialog.setVisible(true);
1834 // bring up graduated chooser.
1835 chooser = new FeatureColourChooser(me.fr, type);
1836 chooser.setRequestFocusEnabled(true);
1837 chooser.requestFocus();
1838 chooser.addActionListener(this);
1840 // Make the renderer reappear.
1841 fireEditingStopped();
1845 { // User pressed dialog's "OK" button.
1846 if (currentColor.isSimpleColour())
1848 currentColor = new FeatureColour(colorChooser.getColor());
1852 currentColor = chooser.getLastColour();
1854 me.table.setValueAt(getCellEditorValue(), selectedRow, 1);
1855 fireEditingStopped();
1856 me.table.validate();
1860 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1862 public Object getCellEditorValue()
1864 return currentColor;
1867 // Implement the one method defined by TableCellEditor.
1869 public Component getTableCellEditorComponent(JTable table, Object value,
1870 boolean isSelected, int row, int column)
1872 currentColor = (FeatureColourI) value;
1873 this.selectedRow = row;
1874 type = me.table.getValueAt(row, 0).toString();
1875 button.setOpaque(true);
1876 button.setBackground(me.getBackground());
1877 if (!currentColor.isSimpleColour())
1879 JLabel btn = new JLabel();
1880 btn.setSize(button.getSize());
1881 FeatureSettings.renderGraduatedColor(btn, currentColor);
1882 button.setBackground(btn.getBackground());
1883 button.setIcon(btn.getIcon());
1884 button.setText(btn.getText());
1889 button.setIcon(null);
1890 button.setBackground(currentColor.getColour());