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<String>();
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<String, float[]>();
567 // TODO: change avWidth calculation to 'per-sequence' average and use long
570 Set<String> displayableTypes = new HashSet<String>();
571 Set<String> foundGroups = new HashSet<String>();
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<String>();
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);
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 fr.setTransparency((100 - transparency.getValue()) / 100f);
1247 if (!inConstruction)
1249 fr.setTransparency((100 - transparency.getValue()) / 100f);
1250 af.alignPanel.paintAlignment(true);
1255 transparency.setMaximum(70);
1256 transparency.setToolTipText(
1257 MessageManager.getString("label.transparency_tip"));
1258 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1259 fetchDAS.addActionListener(new ActionListener()
1262 public void actionPerformed(ActionEvent e)
1264 fetchDAS_actionPerformed(e);
1267 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1268 saveDAS.addActionListener(new ActionListener()
1271 public void actionPerformed(ActionEvent e)
1273 saveDAS_actionPerformed(e);
1276 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1277 dasSettingsPane.setBorder(null);
1278 cancelDAS.setEnabled(false);
1279 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1280 cancelDAS.addActionListener(new ActionListener()
1283 public void actionPerformed(ActionEvent e)
1285 cancelDAS_actionPerformed(e);
1288 this.add(tabbedPane, java.awt.BorderLayout.CENTER);
1289 tabbedPane.addTab(MessageManager.getString("label.feature_settings"),
1291 tabbedPane.addTab(MessageManager.getString("label.das_settings"),
1293 bigPanel.add(transPanel, java.awt.BorderLayout.SOUTH);
1294 transbuttons.add(optimizeOrder);
1295 transbuttons.add(invert);
1296 transbuttons.add(sortByScore);
1297 transbuttons.add(sortByDens);
1298 transbuttons.add(help);
1299 JPanel sliderPanel = new JPanel();
1300 sliderPanel.add(transparency);
1301 transPanel.add(transparency);
1302 transPanel.add(transbuttons);
1303 buttonPanel.add(ok);
1304 buttonPanel.add(cancel);
1305 buttonPanel.add(loadColours);
1306 buttonPanel.add(saveColours);
1307 bigPanel.add(scrollPane, java.awt.BorderLayout.CENTER);
1308 dasSettingsPane.add(dasButtonPanel, java.awt.BorderLayout.SOUTH);
1309 dasButtonPanel.add(fetchDAS);
1310 dasButtonPanel.add(cancelDAS);
1311 dasButtonPanel.add(saveDAS);
1312 settingsPane.add(bigPanel, java.awt.BorderLayout.CENTER);
1313 settingsPane.add(buttonPanel, java.awt.BorderLayout.SOUTH);
1316 public void fetchDAS_actionPerformed(ActionEvent e)
1318 fetchDAS.setEnabled(false);
1319 cancelDAS.setEnabled(true);
1320 dassourceBrowser.setGuiEnabled(false);
1321 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1322 .getSelectedSources();
1323 doDasFeatureFetch(selectedSources, true, true);
1327 * get the features from selectedSources for all or the current selection
1329 * @param selectedSources
1330 * @param checkDbRefs
1331 * @param promptFetchDbRefs
1333 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1334 boolean checkDbRefs, boolean promptFetchDbRefs)
1336 SequenceI[] dataset, seqs;
1338 AlignmentViewport vp = af.getViewport();
1339 if (vp.getSelectionGroup() != null
1340 && vp.getSelectionGroup().getSize() > 0)
1342 iSize = vp.getSelectionGroup().getSize();
1343 dataset = new SequenceI[iSize];
1344 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1348 iSize = vp.getAlignment().getHeight();
1349 seqs = vp.getAlignment().getSequencesArray();
1352 dataset = new SequenceI[iSize];
1353 for (int i = 0; i < iSize; i++)
1355 dataset[i] = seqs[i].getDatasetSequence();
1358 cancelDAS.setEnabled(true);
1359 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1360 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1361 af.getViewport().setShowSequenceFeatures(true);
1362 af.showSeqFeatures.setSelected(true);
1366 * blocking call to initialise the das source browser
1368 public void initDasSources()
1370 dassourceBrowser.initDasSources();
1374 * examine the current list of das sources and return any matching the given
1375 * nicknames in sources
1378 * Vector of Strings to resolve to DAS source nicknames.
1379 * @return sources that are present in source list.
1381 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1383 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1387 * get currently selected das sources. ensure you have called initDasSources
1388 * before calling this.
1390 * @return vector of selected das source nicknames
1392 public Vector<jalviewSourceI> getSelectedSources()
1394 return dassourceBrowser.getSelectedSources();
1398 * properly initialise DAS fetcher and then initiate a new thread to fetch
1399 * features from the named sources (rather than any turned on by default)
1403 * if true then runs in same thread, otherwise passes to the Swing
1406 public void fetchDasFeatures(Vector<String> sources, boolean block)
1409 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1410 .resolveSourceNicknames(sources);
1411 if (resolved.size() == 0)
1413 resolved = dassourceBrowser.getSelectedSources();
1415 if (resolved.size() > 0)
1417 final List<jalviewSourceI> dassources = resolved;
1418 fetchDAS.setEnabled(false);
1419 // cancelDAS.setEnabled(true); doDasFetch does this.
1420 Runnable fetcher = new Runnable()
1426 doDasFeatureFetch(dassources, true, false);
1436 SwingUtilities.invokeLater(fetcher);
1441 public void saveDAS_actionPerformed(ActionEvent e)
1444 .saveProperties(jalview.bin.Cache.applicationProperties);
1447 public void complete()
1449 fetchDAS.setEnabled(true);
1450 cancelDAS.setEnabled(false);
1451 dassourceBrowser.setGuiEnabled(true);
1455 public void cancelDAS_actionPerformed(ActionEvent e)
1457 if (dasFeatureFetcher != null)
1459 dasFeatureFetcher.cancel();
1464 public void noDasSourceActive()
1467 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1468 MessageManager.getString("label.no_das_sources_selected_warn"),
1469 MessageManager.getString("label.no_das_sources_selected_title"),
1470 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1473 // ///////////////////////////////////////////////////////////////////////
1474 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1475 // ///////////////////////////////////////////////////////////////////////
1476 class FeatureTableModel extends AbstractTableModel
1478 FeatureTableModel(Object[][] data)
1483 private String[] columnNames = {
1484 MessageManager.getString("label.feature_type"),
1485 MessageManager.getString("action.colour"),
1486 MessageManager.getString("label.display") };
1488 private Object[][] data;
1490 public Object[][] getData()
1495 public void setData(Object[][] data)
1501 public int getColumnCount()
1503 return columnNames.length;
1506 public Object[] getRow(int row)
1512 public int getRowCount()
1518 public String getColumnName(int col)
1520 return columnNames[col];
1524 public Object getValueAt(int row, int col)
1526 return data[row][col];
1530 public Class getColumnClass(int c)
1532 return getValueAt(0, c).getClass();
1536 public boolean isCellEditable(int row, int col)
1538 return col == 0 ? false : true;
1542 public void setValueAt(Object value, int row, int col)
1544 data[row][col] = value;
1545 fireTableCellUpdated(row, col);
1546 updateFeatureRenderer(data);
1551 class ColorRenderer extends JLabel implements TableCellRenderer
1553 javax.swing.border.Border unselectedBorder = null;
1555 javax.swing.border.Border selectedBorder = null;
1557 final String baseTT = "Click to edit, right/apple click for menu.";
1559 public ColorRenderer()
1561 setOpaque(true); // MUST do this for background to show up.
1562 setHorizontalTextPosition(SwingConstants.CENTER);
1563 setVerticalTextPosition(SwingConstants.CENTER);
1567 public Component getTableCellRendererComponent(JTable tbl, Object color,
1568 boolean isSelected, boolean hasFocus, int row, int column)
1570 FeatureColourI cellColour = (FeatureColourI) color;
1571 // JLabel comp = new JLabel();
1575 // setBounds(getBounds());
1577 setToolTipText(baseTT);
1578 setBackground(tbl.getBackground());
1579 if (!cellColour.isSimpleColour())
1581 Rectangle cr = tbl.getCellRect(row, column, false);
1582 FeatureSettings.renderGraduatedColor(this, cellColour,
1583 (int) cr.getWidth(), (int) cr.getHeight());
1590 newColor = cellColour.getColour();
1591 setBackground(newColor);
1595 if (selectedBorder == null)
1597 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1598 tbl.getSelectionBackground());
1600 setBorder(selectedBorder);
1604 if (unselectedBorder == null)
1606 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1607 tbl.getBackground());
1609 setBorder(unselectedBorder);
1617 * update comp using rendering settings from gcol
1622 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1624 int w = comp.getWidth(), h = comp.getHeight();
1627 w = (int) comp.getPreferredSize().getWidth();
1628 h = (int) comp.getPreferredSize().getHeight();
1635 renderGraduatedColor(comp, gcol, w, h);
1638 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1641 boolean thr = false;
1644 if (gcol.isAboveThreshold())
1648 tt += "Thresholded (Above " + gcol.getThreshold() + ") ";
1650 if (gcol.isBelowThreshold())
1654 tt += "Thresholded (Below " + gcol.getThreshold() + ") ";
1656 if (gcol.isColourByLabel())
1658 tt = "Coloured by label text. " + tt;
1668 Color newColor = gcol.getMaxColour();
1669 comp.setBackground(newColor);
1670 // System.err.println("Width is " + w / 2);
1671 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1672 comp.setIcon(ficon);
1673 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1674 // + newColor.getGreen() + ", " + newColor.getBlue()
1675 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1676 // + ", " + minCol.getBlue() + ")");
1678 comp.setHorizontalAlignment(SwingConstants.CENTER);
1680 if (tt.length() > 0)
1682 if (comp.getToolTipText() == null)
1684 comp.setToolTipText(tt);
1688 comp.setToolTipText(tt + " " + comp.getToolTipText());
1694 class FeatureIcon implements Icon
1696 FeatureColourI gcol;
1700 boolean midspace = false;
1702 int width = 50, height = 20;
1704 int s1, e1; // start and end of midpoint band for thresholded symbol
1706 Color mpcolour = Color.white;
1708 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1728 public int getIconWidth()
1734 public int getIconHeight()
1740 public void paintIcon(Component c, Graphics g, int x, int y)
1743 if (gcol.isColourByLabel())
1746 g.fillRect(0, 0, width, height);
1747 // need an icon here.
1748 g.setColor(gcol.getMaxColour());
1750 g.setFont(new Font("Verdana", Font.PLAIN, 9));
1752 // g.setFont(g.getFont().deriveFont(
1753 // AffineTransform.getScaleInstance(
1754 // width/g.getFontMetrics().stringWidth("Label"),
1755 // height/g.getFontMetrics().getHeight())));
1757 g.drawString(MessageManager.getString("label.label"), 0, 0);
1762 Color minCol = gcol.getMinColour();
1764 g.fillRect(0, 0, s1, height);
1767 g.setColor(Color.white);
1768 g.fillRect(s1, 0, e1 - s1, height);
1770 g.setColor(gcol.getMaxColour());
1771 g.fillRect(0, e1, width - e1, height);
1776 class ColorEditor extends AbstractCellEditor
1777 implements TableCellEditor, ActionListener
1781 FeatureColourI currentColor;
1783 FeatureColourChooser chooser;
1789 JColorChooser colorChooser;
1793 protected static final String EDIT = "edit";
1795 int selectedRow = 0;
1797 public ColorEditor(FeatureSettings me)
1800 // Set up the editor (from the table's point of view),
1801 // which is a button.
1802 // This button brings up the color chooser dialog,
1803 // which is the editor from the user's point of view.
1804 button = new JButton();
1805 button.setActionCommand(EDIT);
1806 button.addActionListener(this);
1807 button.setBorderPainted(false);
1808 // Set up the dialog that the button brings up.
1809 colorChooser = new JColorChooser();
1810 dialog = JColorChooser.createDialog(button, "Select new Colour", true, // modal
1811 colorChooser, this, // OK button handler
1812 null); // no CANCEL button handler
1816 * Handles events from the editor button and from the dialog's OK button.
1819 public void actionPerformed(ActionEvent e)
1822 if (EDIT.equals(e.getActionCommand()))
1824 // The user has clicked the cell, so
1825 // bring up the dialog.
1826 if (currentColor.isSimpleColour())
1828 // bring up simple color chooser
1829 button.setBackground(currentColor.getColour());
1830 colorChooser.setColor(currentColor.getColour());
1831 dialog.setVisible(true);
1835 // bring up graduated chooser.
1836 chooser = new FeatureColourChooser(me.fr, type);
1837 chooser.setRequestFocusEnabled(true);
1838 chooser.requestFocus();
1839 chooser.addActionListener(this);
1841 // Make the renderer reappear.
1842 fireEditingStopped();
1846 { // User pressed dialog's "OK" button.
1847 if (currentColor.isSimpleColour())
1849 currentColor = new FeatureColour(colorChooser.getColor());
1853 currentColor = chooser.getLastColour();
1855 me.table.setValueAt(getCellEditorValue(), selectedRow, 1);
1856 fireEditingStopped();
1857 me.table.validate();
1861 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1863 public Object getCellEditorValue()
1865 return currentColor;
1868 // Implement the one method defined by TableCellEditor.
1870 public Component getTableCellEditorComponent(JTable table, Object value,
1871 boolean isSelected, int row, int column)
1873 currentColor = (FeatureColourI) value;
1874 this.selectedRow = row;
1875 type = me.table.getValueAt(row, 0).toString();
1876 button.setOpaque(true);
1877 button.setBackground(me.getBackground());
1878 if (!currentColor.isSimpleColour())
1880 JLabel btn = new JLabel();
1881 btn.setSize(button.getSize());
1882 FeatureSettings.renderGraduatedColor(btn, currentColor);
1883 button.setBackground(btn.getBackground());
1884 button.setIcon(btn.getIcon());
1885 button.setText(btn.getText());
1890 button.setIcon(null);
1891 button.setBackground(currentColor.getColour());