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;
140 public FeatureSettings(AlignFrame af)
143 fr = af.getFeatureRenderer();
144 // allow transparency to be recovered
145 transparency.setMaximum(100
146 - (int) ((originalTransparency = fr.getTransparency()) * 100));
151 } catch (Exception ex)
153 ex.printStackTrace();
159 public String getToolTipText(MouseEvent e)
161 if (table.columnAtPoint(e.getPoint()) == 0)
164 * Tooltip for feature name only
166 return JvSwingUtils.wrapTooltip(true, MessageManager
167 .getString("label.feature_settings_click_drag"));
172 table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
173 table.setFont(new Font("Verdana", Font.PLAIN, 12));
174 table.setDefaultRenderer(Color.class, new ColorRenderer());
176 table.setDefaultEditor(Color.class, new ColorEditor(this));
178 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
179 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
180 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
182 table.addMouseListener(new MouseAdapter()
185 public void mousePressed(MouseEvent evt)
187 selectedRow = table.rowAtPoint(evt.getPoint());
188 if (evt.isPopupTrigger())
190 popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
191 table.getValueAt(selectedRow, 1), fr.getMinMax(),
192 evt.getX(), evt.getY());
194 else if (evt.getClickCount() == 2)
196 boolean invertSelection = evt.isAltDown();
197 boolean toggleSelection = Platform.isControlDown(evt);
198 boolean extendSelection = evt.isShiftDown();
199 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
200 invertSelection, extendSelection, toggleSelection,
201 (String) table.getValueAt(selectedRow, 0));
205 // isPopupTrigger fires on mouseReleased on Windows
207 public void mouseReleased(MouseEvent evt)
209 selectedRow = table.rowAtPoint(evt.getPoint());
210 if (evt.isPopupTrigger())
212 popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
213 table.getValueAt(selectedRow, 1), fr.getMinMax(),
214 evt.getX(), evt.getY());
219 table.addMouseMotionListener(new MouseMotionAdapter()
222 public void mouseDragged(MouseEvent evt)
224 int newRow = table.rowAtPoint(evt.getPoint());
225 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
228 * reposition 'selectedRow' to 'newRow' (the dragged to location)
229 * this could be more than one row away for a very fast drag action
230 * so just swap it with adjacent rows until we get it there
232 Object[][] data = ((FeatureTableModel) table.getModel())
234 int direction = newRow < selectedRow ? -1 : 1;
235 for (int i = selectedRow; i != newRow; i += direction)
237 Object[] temp = data[i];
238 data[i] = data[i + direction];
239 data[i + direction] = temp;
241 updateFeatureRenderer(data);
243 selectedRow = newRow;
247 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
248 // MessageManager.getString("label.feature_settings_click_drag")));
249 scrollPane.setViewportView(table);
251 dassourceBrowser = new DasSourceBrowser(this);
252 dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
254 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
256 fr.findAllFeatures(true); // display everything!
259 discoverAllFeatureData();
260 final PropertyChangeListener change;
261 final FeatureSettings fs = this;
262 fr.addPropertyChangeListener(change = new PropertyChangeListener()
265 public void propertyChange(PropertyChangeEvent evt)
267 if (!fs.resettingTable && !fs.handlingUpdate)
269 fs.handlingUpdate = true;
270 fs.resetTable(null); // new groups may be added with new seuqence
271 // feature types only
272 fs.handlingUpdate = false;
278 frame = new JInternalFrame();
279 frame.setContentPane(this);
280 if (Platform.isAMac())
282 Desktop.addInternalFrame(frame,
283 MessageManager.getString("label.sequence_feature_settings"),
288 Desktop.addInternalFrame(frame,
289 MessageManager.getString("label.sequence_feature_settings"),
292 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
294 frame.addInternalFrameListener(
295 new javax.swing.event.InternalFrameAdapter()
298 public void internalFrameClosed(
299 javax.swing.event.InternalFrameEvent evt)
301 fr.removePropertyChangeListener(change);
302 dassourceBrowser.fs = null;
305 frame.setLayer(JLayeredPane.PALETTE_LAYER);
308 protected void popupSort(final int selectedRow, final String type,
309 final Object typeCol, final Map<String, float[][]> minmax, int x,
312 final FeatureColourI featureColour = (FeatureColourI) typeCol;
314 JPopupMenu men = new JPopupMenu(MessageManager
315 .formatMessage("label.settings_for_param", new String[]
317 JMenuItem scr = new JMenuItem(
318 MessageManager.getString("label.sort_by_score"));
320 final FeatureSettings me = this;
321 scr.addActionListener(new ActionListener()
325 public void actionPerformed(ActionEvent e)
328 .sortAlignmentByFeatureScore(Arrays.asList(new String[]
333 JMenuItem dens = new JMenuItem(
334 MessageManager.getString("label.sort_by_density"));
335 dens.addActionListener(new ActionListener()
339 public void actionPerformed(ActionEvent e)
342 .sortAlignmentByFeatureDensity(Arrays.asList(new String[]
350 final float[][] typeMinMax = minmax.get(type);
352 * final JCheckBoxMenuItem chb = new JCheckBoxMenuItem("Vary Height"); //
353 * this is broken at the moment and isn't that useful anyway!
354 * chb.setSelected(minmax.get(type) != null); chb.addActionListener(new
357 * public void actionPerformed(ActionEvent e) {
358 * chb.setState(chb.getState()); if (chb.getState()) { minmax.put(type,
359 * null); } else { minmax.put(type, typeMinMax); } }
365 if (typeMinMax != null && typeMinMax[0] != null)
367 // if (table.getValueAt(row, column));
368 // graduated colourschemes for those where minmax exists for the
369 // positional features
370 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
372 mxcol.setSelected(!featureColour.isSimpleColour());
374 mxcol.addActionListener(new ActionListener()
376 JColorChooser colorChooser;
379 public void actionPerformed(ActionEvent e)
381 if (e.getSource() == mxcol)
383 if (featureColour.isSimpleColour())
385 FeatureColourChooser fc = new FeatureColourChooser(me.fr,
387 fc.addActionListener(this);
391 // bring up simple color chooser
392 colorChooser = new JColorChooser();
393 JDialog dialog = JColorChooser.createDialog(me,
394 "Select new Colour", true, // modal
395 colorChooser, this, // OK button handler
396 null); // no CANCEL button handler
397 colorChooser.setColor(featureColour.getMaxColour());
398 dialog.setVisible(true);
403 if (e.getSource() instanceof FeatureColourChooser)
405 FeatureColourChooser fc = (FeatureColourChooser) e
407 table.setValueAt(fc.getLastColour(), selectedRow, 1);
412 // probably the color chooser!
413 table.setValueAt(new FeatureColour(colorChooser.getColor()),
416 me.updateFeatureRenderer(
417 ((FeatureTableModel) table.getModel()).getData(),
426 JMenuItem selCols = new JMenuItem(
427 MessageManager.getString("label.select_columns_containing"));
428 selCols.addActionListener(new ActionListener()
431 public void actionPerformed(ActionEvent arg0)
433 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
437 JMenuItem clearCols = new JMenuItem(MessageManager
438 .getString("label.select_columns_not_containing"));
439 clearCols.addActionListener(new ActionListener()
442 public void actionPerformed(ActionEvent arg0)
444 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
448 JMenuItem hideCols = new JMenuItem(
449 MessageManager.getString("label.hide_columns_containing"));
450 hideCols.addActionListener(new ActionListener()
453 public void actionPerformed(ActionEvent arg0)
455 fr.ap.alignFrame.hideFeatureColumns(type, true);
458 JMenuItem hideOtherCols = new JMenuItem(
459 MessageManager.getString("label.hide_columns_not_containing"));
460 hideOtherCols.addActionListener(new ActionListener()
463 public void actionPerformed(ActionEvent arg0)
465 fr.ap.alignFrame.hideFeatureColumns(type, false);
471 men.add(hideOtherCols);
472 men.show(table, x, y);
476 * true when Feature Settings are updating from feature renderer
478 private boolean handlingUpdate = false;
481 * holds {featureCount, totalExtent} for each feature type
483 Map<String, float[]> typeWidth = null;
486 synchronized public void discoverAllFeatureData()
488 Set<String> allGroups = new HashSet<String>();
489 AlignmentI alignment = af.getViewport().getAlignment();
491 for (int i = 0; i < alignment.getHeight(); i++)
493 SequenceI seq = alignment.getSequenceAt(i);
494 for (String group : seq.getFeatures().getFeatureGroups(true))
496 if (group != null && !allGroups.contains(group))
498 allGroups.add(group);
499 checkGroupState(group);
510 * Synchronise gui group list and check visibility of group
513 * @return true if group is visible
515 private boolean checkGroupState(String group)
517 boolean visible = fr.checkGroupVisibility(group, true);
519 for (int g = 0; g < groupPanel.getComponentCount(); g++)
521 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
523 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
528 final String grp = group;
529 final JCheckBox check = new JCheckBox(group, visible);
530 check.setFont(new Font("Serif", Font.BOLD, 12));
531 check.setToolTipText(group);
532 check.addItemListener(new ItemListener()
535 public void itemStateChanged(ItemEvent evt)
537 fr.setGroupVisibility(check.getText(), check.isSelected());
538 af.alignPanel.getSeqPanel().seqCanvas.repaint();
539 if (af.alignPanel.overviewPanel != null)
541 af.alignPanel.overviewPanel.updateOverviewImage();
544 resetTable(new String[] { grp });
547 groupPanel.add(check);
551 boolean resettingTable = false;
553 synchronized void resetTable(String[] groupChanged)
559 resettingTable = true;
560 typeWidth = new Hashtable<String, float[]>();
561 // TODO: change avWidth calculation to 'per-sequence' average and use long
564 Set<String> displayableTypes = new HashSet<String>();
565 Set<String> foundGroups = new HashSet<String>();
568 * determine which feature types may be visible depending on
569 * which groups are selected, and recompute average width data
571 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
574 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
577 * get the sequence's groups for positional features
578 * and keep track of which groups are visible
580 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
581 Set<String> visibleGroups = new HashSet<String>();
582 for (String group : groups)
584 if (group == null || checkGroupState(group))
586 visibleGroups.add(group);
589 foundGroups.addAll(groups);
592 * get distinct feature types for visible groups
593 * record distinct visible types, and their count and total length
595 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
596 visibleGroups.toArray(new String[visibleGroups.size()]));
597 for (String type : types)
599 displayableTypes.add(type);
600 float[] avWidth = typeWidth.get(type);
603 avWidth = new float[2];
604 typeWidth.put(type, avWidth);
606 // todo this could include features with a non-visible group
607 // - do we greatly care?
608 // todo should we include non-displayable features here, and only
609 // update when features are added?
610 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
611 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
615 Object[][] data = new Object[displayableTypes.size()][3];
618 if (fr.hasRenderOrder())
622 fr.findAllFeatures(groupChanged != null); // prod to update
623 // colourschemes. but don't
625 // First add the checks in the previous render order,
626 // in case the window has been closed and reopened
628 List<String> frl = fr.getRenderOrder();
629 for (int ro = frl.size() - 1; ro > -1; ro--)
631 String type = frl.get(ro);
633 if (!displayableTypes.contains(type))
638 data[dataIndex][0] = type;
639 data[dataIndex][1] = fr.getFeatureStyle(type);
640 data[dataIndex][2] = new Boolean(
641 af.getViewport().getFeaturesDisplayed().isVisible(type));
643 displayableTypes.remove(type);
648 * process any extra features belonging only to
649 * a group which was just selected
651 while (!displayableTypes.isEmpty())
653 String type = displayableTypes.iterator().next();
654 data[dataIndex][0] = type;
656 data[dataIndex][1] = fr.getFeatureStyle(type);
657 if (data[dataIndex][1] == null)
659 // "Colour has been updated in another view!!"
660 fr.clearRenderOrder();
664 data[dataIndex][2] = new Boolean(true);
666 displayableTypes.remove(type);
669 if (originalData == null)
671 originalData = new Object[data.length][3];
672 for (int i = 0; i < data.length; i++)
674 System.arraycopy(data[i], 0, originalData[i], 0, 3);
679 updateOriginalData(data);
682 table.setModel(new FeatureTableModel(data));
683 table.getColumnModel().getColumn(0).setPreferredWidth(200);
685 groupPanel.setLayout(
686 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
687 pruneGroups(foundGroups);
688 groupPanel.validate();
690 updateFeatureRenderer(data, groupChanged != null);
691 resettingTable = false;
695 * Updates 'originalData' (used for restore on Cancel) if we detect that
696 * changes have been made outwith this dialog
698 * <li>a new feature type added (and made visible)</li>
699 * <li>a feature colour changed (in the Amend Features dialog)</li>
704 protected void updateOriginalData(Object[][] foundData)
706 // todo LinkedHashMap instead of Object[][] would be nice
708 Object[][] currentData = ((FeatureTableModel) table.getModel())
710 for (Object[] row : foundData)
712 String type = (String) row[0];
713 boolean found = false;
714 for (Object[] current : currentData)
716 if (type.equals(current[0]))
720 * currently dependent on object equality here;
721 * really need an equals method on FeatureColour
723 if (!row[1].equals(current[1]))
726 * feature colour has changed externally - update originalData
728 for (Object[] original : originalData)
730 if (type.equals(original[0]))
732 original[1] = row[1];
743 * new feature detected - add to original data (on top)
745 Object[][] newData = new Object[originalData.length + 1][3];
746 for (int i = 0; i < originalData.length; i++)
748 System.arraycopy(originalData[i], 0, newData[i + 1], 0, 3);
751 originalData = newData;
757 * Remove from the groups panel any checkboxes for groups that are not in the
758 * foundGroups set. This enables removing a group from the display when the
759 * last feature in that group is deleted.
763 protected void pruneGroups(Set<String> foundGroups)
765 for (int g = 0; g < groupPanel.getComponentCount(); g++)
767 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
768 if (!foundGroups.contains(checkbox.getText()))
770 groupPanel.remove(checkbox);
776 * reorder data based on the featureRenderers global priority list.
780 private void ensureOrder(Object[][] data)
782 boolean sort = false;
783 float[] order = new float[data.length];
784 for (int i = 0; i < order.length; i++)
786 order[i] = fr.getOrder(data[i][0].toString());
789 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
793 sort = sort || order[i - 1] > order[i];
798 jalview.util.QuickSort.sort(order, data);
804 JalviewFileChooser chooser = new JalviewFileChooser("fc",
805 "Sequence Feature Colours");
806 chooser.setFileView(new JalviewFileView());
807 chooser.setDialogTitle(
808 MessageManager.getString("label.load_feature_colours"));
809 chooser.setToolTipText(MessageManager.getString("action.load"));
811 int value = chooser.showOpenDialog(this);
813 if (value == JalviewFileChooser.APPROVE_OPTION)
815 File file = chooser.getSelectedFile();
819 InputStreamReader in = new InputStreamReader(
820 new FileInputStream(file), "UTF-8");
822 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
824 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
827 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
830 Color mincol = null, maxcol = null;
833 mincol = new Color(Integer.parseInt(newcol.getMinRGB(), 16));
834 maxcol = new Color(Integer.parseInt(newcol.getRGB(), 16));
836 } catch (Exception e)
838 Cache.log.warn("Couldn't parse out graduated feature color.",
841 FeatureColourI gcol = new FeatureColour(mincol, maxcol,
842 newcol.getMin(), newcol.getMax());
843 if (newcol.hasAutoScale())
845 gcol.setAutoScaled(newcol.getAutoScale());
847 if (newcol.hasColourByLabel())
849 gcol.setColourByLabel(newcol.getColourByLabel());
851 if (newcol.hasThreshold())
853 gcol.setThreshold(newcol.getThreshold());
855 if (newcol.getThreshType().length() > 0)
857 String ttyp = newcol.getThreshType();
858 if (ttyp.equalsIgnoreCase("ABOVE"))
860 gcol.setAboveThreshold(true);
862 if (ttyp.equalsIgnoreCase("BELOW"))
864 gcol.setBelowThreshold(true);
867 fr.setColour(name = newcol.getName(), gcol);
871 Color color = new Color(
872 Integer.parseInt(jucs.getColour(i).getRGB(), 16));
873 fr.setColour(name = jucs.getColour(i).getName(),
874 new FeatureColour(color));
876 fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
881 Object[][] data = ((FeatureTableModel) table.getModel())
884 updateFeatureRenderer(data, false);
887 } catch (Exception ex)
889 System.out.println("Error loading User Colour File\n" + ex);
896 JalviewFileChooser chooser = new JalviewFileChooser("fc",
897 "Sequence Feature Colours");
898 chooser.setFileView(new JalviewFileView());
899 chooser.setDialogTitle(
900 MessageManager.getString("label.save_feature_colours"));
901 chooser.setToolTipText(MessageManager.getString("action.save"));
903 int value = chooser.showSaveDialog(this);
905 if (value == JalviewFileChooser.APPROVE_OPTION)
907 String choice = chooser.getSelectedFile().getPath();
908 jalview.schemabinding.version2.JalviewUserColours ucs = new jalview.schemabinding.version2.JalviewUserColours();
909 ucs.setSchemeName("Sequence Features");
912 PrintWriter out = new PrintWriter(new OutputStreamWriter(
913 new FileOutputStream(choice), "UTF-8"));
915 Set<String> fr_colours = fr.getAllFeatureColours();
916 Iterator<String> e = fr_colours.iterator();
917 float[] sortOrder = new float[fr_colours.size()];
918 String[] sortTypes = new String[fr_colours.size()];
922 sortTypes[i] = e.next();
923 sortOrder[i] = fr.getOrder(sortTypes[i]);
926 QuickSort.sort(sortOrder, sortTypes);
928 for (i = 0; i < sortTypes.length; i++)
930 jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
931 col.setName(sortTypes[i]);
932 FeatureColourI fcol = fr.getFeatureStyle(sortTypes[i]);
933 if (fcol.isSimpleColour())
935 col.setRGB(Format.getHexString(fcol.getColour()));
939 col.setRGB(Format.getHexString(fcol.getMaxColour()));
940 col.setMin(fcol.getMin());
941 col.setMax(fcol.getMax());
943 jalview.util.Format.getHexString(fcol.getMinColour()));
944 col.setAutoScale(fcol.isAutoScaled());
945 col.setThreshold(fcol.getThreshold());
946 col.setColourByLabel(fcol.isColourByLabel());
947 col.setThreshType(fcol.isAboveThreshold() ? "ABOVE"
948 : (fcol.isBelowThreshold() ? "BELOW" : "NONE"));
954 } catch (Exception ex)
956 ex.printStackTrace();
961 public void invertSelection()
963 for (int i = 0; i < table.getRowCount(); i++)
965 Boolean value = (Boolean) table.getValueAt(i, 2);
967 table.setValueAt(new Boolean(!value.booleanValue()), i, 2);
971 public void orderByAvWidth()
973 if (table == null || table.getModel() == null)
977 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
978 float[] width = new float[data.length];
982 for (int i = 0; i < data.length; i++)
984 awidth = typeWidth.get(data[i][0]);
987 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
988 // weight - but have to make per
989 // sequence, too (awidth[2])
990 // if (width[i]==1) // hack to distinguish single width sequences.
1002 boolean sort = false;
1003 for (int i = 0; i < width.length; i++)
1005 // awidth = (float[]) typeWidth.get(data[i][0]);
1008 width[i] = fr.getOrder(data[i][0].toString());
1011 width[i] = fr.setOrder(data[i][0].toString(), i / data.length);
1016 width[i] /= max; // normalize
1017 fr.setOrder(data[i][0].toString(), width[i]); // store for later
1021 sort = sort || width[i - 1] > width[i];
1026 jalview.util.QuickSort.sort(width, data);
1027 // update global priority order
1030 updateFeatureRenderer(data, false);
1038 frame.setClosed(true);
1039 } catch (Exception exe)
1045 public void updateFeatureRenderer(Object[][] data)
1047 updateFeatureRenderer(data, true);
1051 * Update the priority order of features; only repaint if this changed the
1052 * order of visible features
1057 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1059 if (fr.setFeaturePriority(data, visibleNew))
1061 af.alignPanel.paintAlignment(true);
1065 int selectedRow = -1;
1067 JTabbedPane tabbedPane = new JTabbedPane();
1069 BorderLayout borderLayout1 = new BorderLayout();
1071 BorderLayout borderLayout2 = new BorderLayout();
1073 BorderLayout borderLayout3 = new BorderLayout();
1075 JPanel bigPanel = new JPanel();
1077 BorderLayout borderLayout4 = new BorderLayout();
1079 JButton invert = new JButton();
1081 JPanel buttonPanel = new JPanel();
1083 JButton cancel = new JButton();
1085 JButton ok = new JButton();
1087 JButton loadColours = new JButton();
1089 JButton saveColours = new JButton();
1091 JPanel dasButtonPanel = new JPanel();
1093 JButton fetchDAS = new JButton();
1095 JButton saveDAS = new JButton();
1097 JButton cancelDAS = new JButton();
1099 JButton optimizeOrder = new JButton();
1101 JButton sortByScore = new JButton();
1103 JButton sortByDens = new JButton();
1105 JButton help = new JButton();
1107 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1109 private void jbInit() throws Exception
1111 this.setLayout(borderLayout1);
1112 settingsPane.setLayout(borderLayout2);
1113 dasSettingsPane.setLayout(borderLayout3);
1114 bigPanel.setLayout(borderLayout4);
1116 groupPanel = new JPanel();
1117 bigPanel.add(groupPanel, BorderLayout.NORTH);
1119 invert.setFont(JvSwingUtils.getLabelFont());
1120 invert.setText(MessageManager.getString("label.invert_selection"));
1121 invert.addActionListener(new ActionListener()
1124 public void actionPerformed(ActionEvent e)
1129 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1130 optimizeOrder.setText(MessageManager.getString("label.optimise_order"));
1131 optimizeOrder.addActionListener(new ActionListener()
1134 public void actionPerformed(ActionEvent e)
1139 sortByScore.setFont(JvSwingUtils.getLabelFont());
1141 .setText(MessageManager.getString("label.seq_sort_by_score"));
1142 sortByScore.addActionListener(new ActionListener()
1145 public void actionPerformed(ActionEvent e)
1147 af.avc.sortAlignmentByFeatureScore(null);
1150 sortByDens.setFont(JvSwingUtils.getLabelFont());
1152 MessageManager.getString("label.sequence_sort_by_density"));
1153 sortByDens.addActionListener(new ActionListener()
1156 public void actionPerformed(ActionEvent e)
1158 af.avc.sortAlignmentByFeatureDensity(null);
1161 help.setFont(JvSwingUtils.getLabelFont());
1162 help.setText(MessageManager.getString("action.help"));
1163 help.addActionListener(new ActionListener()
1166 public void actionPerformed(ActionEvent e)
1170 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1171 } catch (HelpSetException e1)
1173 e1.printStackTrace();
1177 help.setFont(JvSwingUtils.getLabelFont());
1178 help.setText(MessageManager.getString("action.help"));
1179 help.addActionListener(new ActionListener()
1182 public void actionPerformed(ActionEvent e)
1186 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1187 } catch (HelpSetException e1)
1189 e1.printStackTrace();
1193 cancel.setFont(JvSwingUtils.getLabelFont());
1194 cancel.setText(MessageManager.getString("action.cancel"));
1195 cancel.addActionListener(new ActionListener()
1198 public void actionPerformed(ActionEvent e)
1200 fr.setTransparency(originalTransparency);
1201 updateFeatureRenderer(originalData);
1205 ok.setFont(JvSwingUtils.getLabelFont());
1206 ok.setText(MessageManager.getString("action.ok"));
1207 ok.addActionListener(new ActionListener()
1210 public void actionPerformed(ActionEvent e)
1215 loadColours.setFont(JvSwingUtils.getLabelFont());
1216 loadColours.setText(MessageManager.getString("label.load_colours"));
1217 loadColours.addActionListener(new ActionListener()
1220 public void actionPerformed(ActionEvent e)
1225 saveColours.setFont(JvSwingUtils.getLabelFont());
1226 saveColours.setText(MessageManager.getString("label.save_colours"));
1227 saveColours.addActionListener(new ActionListener()
1230 public void actionPerformed(ActionEvent e)
1235 transparency.addChangeListener(new ChangeListener()
1238 public void stateChanged(ChangeEvent evt)
1240 fr.setTransparency((100 - transparency.getValue()) / 100f);
1241 af.alignPanel.paintAlignment(true);
1245 transparency.setMaximum(70);
1246 transparency.setToolTipText(
1247 MessageManager.getString("label.transparency_tip"));
1248 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1249 fetchDAS.addActionListener(new ActionListener()
1252 public void actionPerformed(ActionEvent e)
1254 fetchDAS_actionPerformed(e);
1257 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1258 saveDAS.addActionListener(new ActionListener()
1261 public void actionPerformed(ActionEvent e)
1263 saveDAS_actionPerformed(e);
1266 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1267 dasSettingsPane.setBorder(null);
1268 cancelDAS.setEnabled(false);
1269 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1270 cancelDAS.addActionListener(new ActionListener()
1273 public void actionPerformed(ActionEvent e)
1275 cancelDAS_actionPerformed(e);
1278 this.add(tabbedPane, java.awt.BorderLayout.CENTER);
1279 tabbedPane.addTab(MessageManager.getString("label.feature_settings"),
1281 tabbedPane.addTab(MessageManager.getString("label.das_settings"),
1283 bigPanel.add(transPanel, java.awt.BorderLayout.SOUTH);
1284 transbuttons.add(optimizeOrder);
1285 transbuttons.add(invert);
1286 transbuttons.add(sortByScore);
1287 transbuttons.add(sortByDens);
1288 transbuttons.add(help);
1289 JPanel sliderPanel = new JPanel();
1290 sliderPanel.add(transparency);
1291 transPanel.add(transparency);
1292 transPanel.add(transbuttons);
1293 buttonPanel.add(ok);
1294 buttonPanel.add(cancel);
1295 buttonPanel.add(loadColours);
1296 buttonPanel.add(saveColours);
1297 bigPanel.add(scrollPane, java.awt.BorderLayout.CENTER);
1298 dasSettingsPane.add(dasButtonPanel, java.awt.BorderLayout.SOUTH);
1299 dasButtonPanel.add(fetchDAS);
1300 dasButtonPanel.add(cancelDAS);
1301 dasButtonPanel.add(saveDAS);
1302 settingsPane.add(bigPanel, java.awt.BorderLayout.CENTER);
1303 settingsPane.add(buttonPanel, java.awt.BorderLayout.SOUTH);
1306 public void fetchDAS_actionPerformed(ActionEvent e)
1308 fetchDAS.setEnabled(false);
1309 cancelDAS.setEnabled(true);
1310 dassourceBrowser.setGuiEnabled(false);
1311 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1312 .getSelectedSources();
1313 doDasFeatureFetch(selectedSources, true, true);
1317 * get the features from selectedSources for all or the current selection
1319 * @param selectedSources
1320 * @param checkDbRefs
1321 * @param promptFetchDbRefs
1323 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1324 boolean checkDbRefs, boolean promptFetchDbRefs)
1326 SequenceI[] dataset, seqs;
1328 AlignmentViewport vp = af.getViewport();
1329 if (vp.getSelectionGroup() != null
1330 && vp.getSelectionGroup().getSize() > 0)
1332 iSize = vp.getSelectionGroup().getSize();
1333 dataset = new SequenceI[iSize];
1334 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1338 iSize = vp.getAlignment().getHeight();
1339 seqs = vp.getAlignment().getSequencesArray();
1342 dataset = new SequenceI[iSize];
1343 for (int i = 0; i < iSize; i++)
1345 dataset[i] = seqs[i].getDatasetSequence();
1348 cancelDAS.setEnabled(true);
1349 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1350 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1351 af.getViewport().setShowSequenceFeatures(true);
1352 af.showSeqFeatures.setSelected(true);
1356 * blocking call to initialise the das source browser
1358 public void initDasSources()
1360 dassourceBrowser.initDasSources();
1364 * examine the current list of das sources and return any matching the given
1365 * nicknames in sources
1368 * Vector of Strings to resolve to DAS source nicknames.
1369 * @return sources that are present in source list.
1371 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1373 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1377 * get currently selected das sources. ensure you have called initDasSources
1378 * before calling this.
1380 * @return vector of selected das source nicknames
1382 public Vector<jalviewSourceI> getSelectedSources()
1384 return dassourceBrowser.getSelectedSources();
1388 * properly initialise DAS fetcher and then initiate a new thread to fetch
1389 * features from the named sources (rather than any turned on by default)
1393 * if true then runs in same thread, otherwise passes to the Swing
1396 public void fetchDasFeatures(Vector<String> sources, boolean block)
1399 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1400 .resolveSourceNicknames(sources);
1401 if (resolved.size() == 0)
1403 resolved = dassourceBrowser.getSelectedSources();
1405 if (resolved.size() > 0)
1407 final List<jalviewSourceI> dassources = resolved;
1408 fetchDAS.setEnabled(false);
1409 // cancelDAS.setEnabled(true); doDasFetch does this.
1410 Runnable fetcher = new Runnable()
1416 doDasFeatureFetch(dassources, true, false);
1426 SwingUtilities.invokeLater(fetcher);
1431 public void saveDAS_actionPerformed(ActionEvent e)
1434 .saveProperties(jalview.bin.Cache.applicationProperties);
1437 public void complete()
1439 fetchDAS.setEnabled(true);
1440 cancelDAS.setEnabled(false);
1441 dassourceBrowser.setGuiEnabled(true);
1445 public void cancelDAS_actionPerformed(ActionEvent e)
1447 if (dasFeatureFetcher != null)
1449 dasFeatureFetcher.cancel();
1454 public void noDasSourceActive()
1457 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1458 MessageManager.getString("label.no_das_sources_selected_warn"),
1459 MessageManager.getString("label.no_das_sources_selected_title"),
1460 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1463 // ///////////////////////////////////////////////////////////////////////
1464 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1465 // ///////////////////////////////////////////////////////////////////////
1466 class FeatureTableModel extends AbstractTableModel
1468 FeatureTableModel(Object[][] data)
1473 private String[] columnNames = {
1474 MessageManager.getString("label.feature_type"),
1475 MessageManager.getString("action.colour"),
1476 MessageManager.getString("label.display") };
1478 private Object[][] data;
1480 public Object[][] getData()
1485 public void setData(Object[][] data)
1491 public int getColumnCount()
1493 return columnNames.length;
1496 public Object[] getRow(int row)
1502 public int getRowCount()
1508 public String getColumnName(int col)
1510 return columnNames[col];
1514 public Object getValueAt(int row, int col)
1516 return data[row][col];
1520 public Class getColumnClass(int c)
1522 return getValueAt(0, c).getClass();
1526 public boolean isCellEditable(int row, int col)
1528 return col == 0 ? false : true;
1532 public void setValueAt(Object value, int row, int col)
1534 data[row][col] = value;
1535 fireTableCellUpdated(row, col);
1536 updateFeatureRenderer(data);
1541 class ColorRenderer extends JLabel implements TableCellRenderer
1543 javax.swing.border.Border unselectedBorder = null;
1545 javax.swing.border.Border selectedBorder = null;
1547 final String baseTT = "Click to edit, right/apple click for menu.";
1549 public ColorRenderer()
1551 setOpaque(true); // MUST do this for background to show up.
1552 setHorizontalTextPosition(SwingConstants.CENTER);
1553 setVerticalTextPosition(SwingConstants.CENTER);
1557 public Component getTableCellRendererComponent(JTable tbl, Object color,
1558 boolean isSelected, boolean hasFocus, int row, int column)
1560 FeatureColourI cellColour = (FeatureColourI) color;
1561 // JLabel comp = new JLabel();
1565 // setBounds(getBounds());
1567 setToolTipText(baseTT);
1568 setBackground(tbl.getBackground());
1569 if (!cellColour.isSimpleColour())
1571 Rectangle cr = tbl.getCellRect(row, column, false);
1572 FeatureSettings.renderGraduatedColor(this, cellColour,
1573 (int) cr.getWidth(), (int) cr.getHeight());
1580 newColor = cellColour.getColour();
1581 setBackground(newColor);
1585 if (selectedBorder == null)
1587 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1588 tbl.getSelectionBackground());
1590 setBorder(selectedBorder);
1594 if (unselectedBorder == null)
1596 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1597 tbl.getBackground());
1599 setBorder(unselectedBorder);
1607 * update comp using rendering settings from gcol
1612 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1614 int w = comp.getWidth(), h = comp.getHeight();
1617 w = (int) comp.getPreferredSize().getWidth();
1618 h = (int) comp.getPreferredSize().getHeight();
1625 renderGraduatedColor(comp, gcol, w, h);
1628 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1631 boolean thr = false;
1634 if (gcol.isAboveThreshold())
1638 tt += "Thresholded (Above " + gcol.getThreshold() + ") ";
1640 if (gcol.isBelowThreshold())
1644 tt += "Thresholded (Below " + gcol.getThreshold() + ") ";
1646 if (gcol.isColourByLabel())
1648 tt = "Coloured by label text. " + tt;
1658 Color newColor = gcol.getMaxColour();
1659 comp.setBackground(newColor);
1660 // System.err.println("Width is " + w / 2);
1661 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1662 comp.setIcon(ficon);
1663 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1664 // + newColor.getGreen() + ", " + newColor.getBlue()
1665 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1666 // + ", " + minCol.getBlue() + ")");
1668 comp.setHorizontalAlignment(SwingConstants.CENTER);
1670 if (tt.length() > 0)
1672 if (comp.getToolTipText() == null)
1674 comp.setToolTipText(tt);
1678 comp.setToolTipText(tt + " " + comp.getToolTipText());
1684 class FeatureIcon implements Icon
1686 FeatureColourI gcol;
1690 boolean midspace = false;
1692 int width = 50, height = 20;
1694 int s1, e1; // start and end of midpoint band for thresholded symbol
1696 Color mpcolour = Color.white;
1698 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1718 public int getIconWidth()
1724 public int getIconHeight()
1730 public void paintIcon(Component c, Graphics g, int x, int y)
1733 if (gcol.isColourByLabel())
1736 g.fillRect(0, 0, width, height);
1737 // need an icon here.
1738 g.setColor(gcol.getMaxColour());
1740 g.setFont(new Font("Verdana", Font.PLAIN, 9));
1742 // g.setFont(g.getFont().deriveFont(
1743 // AffineTransform.getScaleInstance(
1744 // width/g.getFontMetrics().stringWidth("Label"),
1745 // height/g.getFontMetrics().getHeight())));
1747 g.drawString(MessageManager.getString("label.label"), 0, 0);
1752 Color minCol = gcol.getMinColour();
1754 g.fillRect(0, 0, s1, height);
1757 g.setColor(Color.white);
1758 g.fillRect(s1, 0, e1 - s1, height);
1760 g.setColor(gcol.getMaxColour());
1761 g.fillRect(0, e1, width - e1, height);
1766 class ColorEditor extends AbstractCellEditor
1767 implements TableCellEditor, ActionListener
1771 FeatureColourI currentColor;
1773 FeatureColourChooser chooser;
1779 JColorChooser colorChooser;
1783 protected static final String EDIT = "edit";
1785 int selectedRow = 0;
1787 public ColorEditor(FeatureSettings me)
1790 // Set up the editor (from the table's point of view),
1791 // which is a button.
1792 // This button brings up the color chooser dialog,
1793 // which is the editor from the user's point of view.
1794 button = new JButton();
1795 button.setActionCommand(EDIT);
1796 button.addActionListener(this);
1797 button.setBorderPainted(false);
1798 // Set up the dialog that the button brings up.
1799 colorChooser = new JColorChooser();
1800 dialog = JColorChooser.createDialog(button, "Select new Colour", true, // modal
1801 colorChooser, this, // OK button handler
1802 null); // no CANCEL button handler
1806 * Handles events from the editor button and from the dialog's OK button.
1809 public void actionPerformed(ActionEvent e)
1812 if (EDIT.equals(e.getActionCommand()))
1814 // The user has clicked the cell, so
1815 // bring up the dialog.
1816 if (currentColor.isSimpleColour())
1818 // bring up simple color chooser
1819 button.setBackground(currentColor.getColour());
1820 colorChooser.setColor(currentColor.getColour());
1821 dialog.setVisible(true);
1825 // bring up graduated chooser.
1826 chooser = new FeatureColourChooser(me.fr, type);
1827 chooser.setRequestFocusEnabled(true);
1828 chooser.requestFocus();
1829 chooser.addActionListener(this);
1831 // Make the renderer reappear.
1832 fireEditingStopped();
1836 { // User pressed dialog's "OK" button.
1837 if (currentColor.isSimpleColour())
1839 currentColor = new FeatureColour(colorChooser.getColor());
1843 currentColor = chooser.getLastColour();
1845 me.table.setValueAt(getCellEditorValue(), selectedRow, 1);
1846 fireEditingStopped();
1847 me.table.validate();
1851 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1853 public Object getCellEditorValue()
1855 return currentColor;
1858 // Implement the one method defined by TableCellEditor.
1860 public Component getTableCellEditorComponent(JTable table, Object value,
1861 boolean isSelected, int row, int column)
1863 currentColor = (FeatureColourI) value;
1864 this.selectedRow = row;
1865 type = me.table.getValueAt(row, 0).toString();
1866 button.setOpaque(true);
1867 button.setBackground(me.getBackground());
1868 if (!currentColor.isSimpleColour())
1870 JLabel btn = new JLabel();
1871 btn.setSize(button.getSize());
1872 FeatureSettings.renderGraduatedColor(btn, currentColor);
1873 button.setBackground(btn.getBackground());
1874 button.setIcon(btn.getIcon());
1875 button.setText(btn.getText());
1880 button.setIcon(null);
1881 button.setBackground(currentColor.getColour());