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 implements
101 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 - (int) ((originalTransparency = fr
146 .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(new javax.swing.event.InternalFrameAdapter()
297 public void internalFrameClosed(
298 javax.swing.event.InternalFrameEvent evt)
300 fr.removePropertyChangeListener(change);
301 dassourceBrowser.fs = null;
304 frame.setLayer(JLayeredPane.PALETTE_LAYER);
307 protected void popupSort(final int selectedRow, final String type,
308 final Object typeCol, final Map<String, float[][]> minmax, int x,
311 final FeatureColourI featureColour = (FeatureColourI) typeCol;
313 JPopupMenu men = new JPopupMenu(MessageManager.formatMessage(
314 "label.settings_for_param", new String[] { type }));
315 JMenuItem scr = new JMenuItem(
316 MessageManager.getString("label.sort_by_score"));
318 final FeatureSettings me = this;
319 scr.addActionListener(new ActionListener()
323 public void actionPerformed(ActionEvent e)
325 me.af.avc.sortAlignmentByFeatureScore(Arrays
326 .asList(new String[] { type }));
330 JMenuItem dens = new JMenuItem(
331 MessageManager.getString("label.sort_by_density"));
332 dens.addActionListener(new ActionListener()
336 public void actionPerformed(ActionEvent e)
338 me.af.avc.sortAlignmentByFeatureDensity(Arrays
339 .asList(new String[] { type }));
346 final float[][] typeMinMax = minmax.get(type);
348 * final JCheckBoxMenuItem chb = new JCheckBoxMenuItem("Vary Height"); //
349 * this is broken at the moment and isn't that useful anyway!
350 * chb.setSelected(minmax.get(type) != null); chb.addActionListener(new
353 * public void actionPerformed(ActionEvent e) {
354 * chb.setState(chb.getState()); if (chb.getState()) { minmax.put(type,
355 * null); } else { minmax.put(type, typeMinMax); } }
361 if (typeMinMax != null && typeMinMax[0] != null)
363 // if (table.getValueAt(row, column));
364 // graduated colourschemes for those where minmax exists for the
365 // positional features
366 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
368 mxcol.setSelected(!featureColour.isSimpleColour());
370 mxcol.addActionListener(new ActionListener()
372 JColorChooser colorChooser;
375 public void actionPerformed(ActionEvent e)
377 if (e.getSource() == mxcol)
379 if (featureColour.isSimpleColour())
381 FeatureColourChooser fc = new FeatureColourChooser(me.fr,
383 fc.addActionListener(this);
387 // bring up simple color chooser
388 colorChooser = new JColorChooser();
389 JDialog dialog = JColorChooser.createDialog(me,
390 "Select new Colour", true, // modal
391 colorChooser, this, // OK button handler
392 null); // no CANCEL button handler
393 colorChooser.setColor(featureColour.getMaxColour());
394 dialog.setVisible(true);
399 if (e.getSource() instanceof FeatureColourChooser)
401 FeatureColourChooser fc = (FeatureColourChooser) e
403 table.setValueAt(fc.getLastColour(), selectedRow, 1);
408 // probably the color chooser!
410 new FeatureColour(colorChooser.getColor()),
413 me.updateFeatureRenderer(
414 ((FeatureTableModel) table.getModel()).getData(),
423 JMenuItem selCols = new JMenuItem(
424 MessageManager.getString("label.select_columns_containing"));
425 selCols.addActionListener(new ActionListener()
428 public void actionPerformed(ActionEvent arg0)
430 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
434 JMenuItem clearCols = new JMenuItem(
435 MessageManager.getString("label.select_columns_not_containing"));
436 clearCols.addActionListener(new ActionListener()
439 public void actionPerformed(ActionEvent arg0)
441 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
445 JMenuItem hideCols = new JMenuItem(
446 MessageManager.getString("label.hide_columns_containing"));
447 hideCols.addActionListener(new ActionListener()
450 public void actionPerformed(ActionEvent arg0)
452 fr.ap.alignFrame.hideFeatureColumns(type, true);
455 JMenuItem hideOtherCols = new JMenuItem(
456 MessageManager.getString("label.hide_columns_not_containing"));
457 hideOtherCols.addActionListener(new ActionListener()
460 public void actionPerformed(ActionEvent arg0)
462 fr.ap.alignFrame.hideFeatureColumns(type, false);
468 men.add(hideOtherCols);
469 men.show(table, x, y);
473 * true when Feature Settings are updating from feature renderer
475 private boolean handlingUpdate = false;
478 * holds {featureCount, totalExtent} for each feature type
480 Map<String, float[]> typeWidth = null;
483 synchronized public void discoverAllFeatureData()
485 Set<String> allGroups = new HashSet<String>();
486 AlignmentI alignment = af.getViewport().getAlignment();
488 for (int i = 0; i < alignment.getHeight(); i++)
490 SequenceI seq = alignment.getSequenceAt(i);
491 for (String group : seq.getFeatures().getFeatureGroups(true))
493 if (group != null && !allGroups.contains(group))
495 allGroups.add(group);
496 checkGroupState(group);
507 * Synchronise gui group list and check visibility of group
510 * @return true if group is visible
512 private boolean checkGroupState(String group)
514 boolean visible = fr.checkGroupVisibility(group, true);
516 for (int g = 0; g < groupPanel.getComponentCount(); g++)
518 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
520 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
525 final String grp = group;
526 final JCheckBox check = new JCheckBox(group, visible);
527 check.setFont(new Font("Serif", Font.BOLD, 12));
528 check.addItemListener(new ItemListener()
531 public void itemStateChanged(ItemEvent evt)
533 fr.setGroupVisibility(check.getText(), check.isSelected());
534 af.alignPanel.getSeqPanel().seqCanvas.repaint();
535 if (af.alignPanel.overviewPanel != null)
537 af.alignPanel.overviewPanel.updateOverviewImage();
540 resetTable(new String[] { grp });
543 groupPanel.add(check);
547 boolean resettingTable = false;
549 synchronized void resetTable(String[] groupChanged)
555 resettingTable = true;
556 typeWidth = new Hashtable<String, float[]>();
557 // TODO: change avWidth calculation to 'per-sequence' average and use long
560 Set<String> displayableTypes = new HashSet<String>();
561 Set<String> foundGroups = new HashSet<String>();
564 * determine which feature types may be visible depending on
565 * which groups are selected, and recompute average width data
567 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
570 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
573 * get the sequence's groups for positional features
574 * and keep track of which groups are visible
576 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
577 Set<String> visibleGroups = new HashSet<String>();
578 for (String group : groups)
580 if (group == null || checkGroupState(group))
582 visibleGroups.add(group);
585 foundGroups.addAll(groups);
588 * get distinct feature types for visible groups
589 * record distinct visible types, and their count and total length
591 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
592 visibleGroups.toArray(new String[visibleGroups.size()]));
593 for (String type : types)
595 displayableTypes.add(type);
596 float[] avWidth = typeWidth.get(type);
599 avWidth = new float[2];
600 typeWidth.put(type, avWidth);
602 // todo this could include features with a non-visible group
603 // - do we greatly care?
604 // todo should we include non-displayable features here, and only
605 // update when features are added?
606 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
607 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
611 Object[][] data = new Object[displayableTypes.size()][3];
614 if (fr.hasRenderOrder())
618 fr.findAllFeatures(groupChanged != null); // prod to update
619 // colourschemes. but don't
621 // First add the checks in the previous render order,
622 // in case the window has been closed and reopened
624 List<String> frl = fr.getRenderOrder();
625 for (int ro = frl.size() - 1; ro > -1; ro--)
627 String type = frl.get(ro);
629 if (!displayableTypes.contains(type))
634 data[dataIndex][0] = type;
635 data[dataIndex][1] = fr.getFeatureStyle(type);
636 data[dataIndex][2] = new Boolean(af.getViewport()
637 .getFeaturesDisplayed().isVisible(type));
639 displayableTypes.remove(type);
644 * process any extra features belonging only to
645 * a group which was just selected
647 while (!displayableTypes.isEmpty())
649 String type = displayableTypes.iterator().next();
650 data[dataIndex][0] = type;
652 data[dataIndex][1] = fr.getFeatureStyle(type);
653 if (data[dataIndex][1] == null)
655 // "Colour has been updated in another view!!"
656 fr.clearRenderOrder();
660 data[dataIndex][2] = new Boolean(true);
662 displayableTypes.remove(type);
665 if (originalData == null)
667 originalData = new Object[data.length][3];
668 for (int i = 0; i < data.length; i++)
670 System.arraycopy(data[i], 0, originalData[i], 0, 3);
675 updateOriginalData(data);
678 table.setModel(new FeatureTableModel(data));
679 table.getColumnModel().getColumn(0).setPreferredWidth(200);
681 groupPanel.setLayout(new GridLayout(fr.getFeatureGroupsSize() / 4 + 1,
683 pruneGroups(foundGroups);
684 groupPanel.validate();
686 updateFeatureRenderer(data, groupChanged != null);
687 resettingTable = false;
691 * Updates 'originalData' (used for restore on Cancel) if we detect that
692 * changes have been made outwith this dialog
694 * <li>a new feature type added (and made visible)</li>
695 * <li>a feature colour changed (in the Amend Features dialog)</li>
700 protected void updateOriginalData(Object[][] foundData)
702 // todo LinkedHashMap instead of Object[][] would be nice
704 Object[][] currentData = ((FeatureTableModel) table.getModel())
706 for (Object[] row : foundData)
708 String type = (String) row[0];
709 boolean found = false;
710 for (Object[] current : currentData)
712 if (type.equals(current[0]))
716 * currently dependent on object equality here;
717 * really need an equals method on FeatureColour
719 if (!row[1].equals(current[1]))
722 * feature colour has changed externally - update originalData
724 for (Object[] original : originalData)
726 if (type.equals(original[0]))
728 original[1] = row[1];
739 * new feature detected - add to original data (on top)
741 Object[][] newData = new Object[originalData.length + 1][3];
742 for (int i = 0; i < originalData.length; i++)
744 System.arraycopy(originalData[i], 0, newData[i + 1], 0, 3);
747 originalData = newData;
753 * Remove from the groups panel any checkboxes for groups that are not in the
754 * foundGroups set. This enables removing a group from the display when the
755 * last feature in that group is deleted.
759 protected void pruneGroups(Set<String> foundGroups)
761 for (int g = 0; g < groupPanel.getComponentCount(); g++)
763 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
764 if (!foundGroups.contains(checkbox.getText()))
766 groupPanel.remove(checkbox);
772 * reorder data based on the featureRenderers global priority list.
776 private void ensureOrder(Object[][] data)
778 boolean sort = false;
779 float[] order = new float[data.length];
780 for (int i = 0; i < order.length; i++)
782 order[i] = fr.getOrder(data[i][0].toString());
785 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
789 sort = sort || order[i - 1] > order[i];
794 jalview.util.QuickSort.sort(order, data);
800 JalviewFileChooser chooser = new JalviewFileChooser("fc",
801 "Sequence Feature Colours");
802 chooser.setFileView(new JalviewFileView());
803 chooser.setDialogTitle(MessageManager
804 .getString("label.load_feature_colours"));
805 chooser.setToolTipText(MessageManager.getString("action.load"));
807 int value = chooser.showOpenDialog(this);
809 if (value == JalviewFileChooser.APPROVE_OPTION)
811 File file = chooser.getSelectedFile();
815 InputStreamReader in = new InputStreamReader(new FileInputStream(
818 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
820 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
823 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
826 Color mincol = null, maxcol = null;
829 mincol = new Color(Integer.parseInt(newcol.getMinRGB(), 16));
830 maxcol = new Color(Integer.parseInt(newcol.getRGB(), 16));
832 } catch (Exception e)
834 Cache.log.warn("Couldn't parse out graduated feature color.",
837 FeatureColourI gcol = new FeatureColour(mincol, maxcol,
838 newcol.getMin(), newcol.getMax());
839 if (newcol.hasAutoScale())
841 gcol.setAutoScaled(newcol.getAutoScale());
843 if (newcol.hasColourByLabel())
845 gcol.setColourByLabel(newcol.getColourByLabel());
847 if (newcol.hasThreshold())
849 gcol.setThreshold(newcol.getThreshold());
851 if (newcol.getThreshType().length() > 0)
853 String ttyp = newcol.getThreshType();
854 if (ttyp.equalsIgnoreCase("ABOVE"))
856 gcol.setAboveThreshold(true);
858 if (ttyp.equalsIgnoreCase("BELOW"))
860 gcol.setBelowThreshold(true);
863 fr.setColour(name = newcol.getName(), gcol);
867 Color color = new Color(
868 Integer.parseInt(jucs.getColour(i).getRGB(), 16));
869 fr.setColour(name = jucs.getColour(i).getName(),
870 new FeatureColour(color));
872 fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
877 Object[][] data = ((FeatureTableModel) table.getModel())
880 updateFeatureRenderer(data, false);
883 } catch (Exception ex)
885 System.out.println("Error loading User Colour File\n" + ex);
892 JalviewFileChooser chooser = new JalviewFileChooser("fc",
893 "Sequence Feature Colours");
894 chooser.setFileView(new JalviewFileView());
895 chooser.setDialogTitle(MessageManager
896 .getString("label.save_feature_colours"));
897 chooser.setToolTipText(MessageManager.getString("action.save"));
899 int value = chooser.showSaveDialog(this);
901 if (value == JalviewFileChooser.APPROVE_OPTION)
903 String choice = chooser.getSelectedFile().getPath();
904 jalview.schemabinding.version2.JalviewUserColours ucs = new jalview.schemabinding.version2.JalviewUserColours();
905 ucs.setSchemeName("Sequence Features");
908 PrintWriter out = new PrintWriter(new OutputStreamWriter(
909 new FileOutputStream(choice), "UTF-8"));
911 Set<String> fr_colours = fr.getAllFeatureColours();
912 Iterator<String> e = fr_colours.iterator();
913 float[] sortOrder = new float[fr_colours.size()];
914 String[] sortTypes = new String[fr_colours.size()];
918 sortTypes[i] = e.next();
919 sortOrder[i] = fr.getOrder(sortTypes[i]);
922 QuickSort.sort(sortOrder, sortTypes);
924 for (i = 0; i < sortTypes.length; i++)
926 jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
927 col.setName(sortTypes[i]);
928 FeatureColourI fcol = fr.getFeatureStyle(sortTypes[i]);
929 if (fcol.isSimpleColour())
931 col.setRGB(Format.getHexString(fcol.getColour()));
935 col.setRGB(Format.getHexString(fcol.getMaxColour()));
936 col.setMin(fcol.getMin());
937 col.setMax(fcol.getMax());
938 col.setMinRGB(jalview.util.Format.getHexString(fcol
940 col.setAutoScale(fcol.isAutoScaled());
941 col.setThreshold(fcol.getThreshold());
942 col.setColourByLabel(fcol.isColourByLabel());
943 col.setThreshType(fcol.isAboveThreshold() ? "ABOVE" : (fcol
944 .isBelowThreshold() ? "BELOW" : "NONE"));
950 } catch (Exception ex)
952 ex.printStackTrace();
957 public void invertSelection()
959 for (int i = 0; i < table.getRowCount(); i++)
961 Boolean value = (Boolean) table.getValueAt(i, 2);
963 table.setValueAt(new Boolean(!value.booleanValue()), i, 2);
967 public void orderByAvWidth()
969 if (table == null || table.getModel() == null)
973 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
974 float[] width = new float[data.length];
978 for (int i = 0; i < data.length; i++)
980 awidth = typeWidth.get(data[i][0]);
983 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
984 // weight - but have to make per
985 // sequence, too (awidth[2])
986 // if (width[i]==1) // hack to distinguish single width sequences.
998 boolean sort = false;
999 for (int i = 0; i < width.length; i++)
1001 // awidth = (float[]) typeWidth.get(data[i][0]);
1004 width[i] = fr.getOrder(data[i][0].toString());
1007 width[i] = fr.setOrder(data[i][0].toString(), i / data.length);
1012 width[i] /= max; // normalize
1013 fr.setOrder(data[i][0].toString(), width[i]); // store for later
1017 sort = sort || width[i - 1] > width[i];
1022 jalview.util.QuickSort.sort(width, data);
1023 // update global priority order
1026 updateFeatureRenderer(data, false);
1034 frame.setClosed(true);
1035 } catch (Exception exe)
1041 public void updateFeatureRenderer(Object[][] data)
1043 updateFeatureRenderer(data, true);
1047 * Update the priority order of features; only repaint if this changed the
1048 * order of visible features
1053 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1055 if (fr.setFeaturePriority(data, visibleNew))
1057 af.alignPanel.paintAlignment(true);
1061 int selectedRow = -1;
1063 JTabbedPane tabbedPane = new JTabbedPane();
1065 BorderLayout borderLayout1 = new BorderLayout();
1067 BorderLayout borderLayout2 = new BorderLayout();
1069 BorderLayout borderLayout3 = new BorderLayout();
1071 JPanel bigPanel = new JPanel();
1073 BorderLayout borderLayout4 = new BorderLayout();
1075 JButton invert = new JButton();
1077 JPanel buttonPanel = new JPanel();
1079 JButton cancel = new JButton();
1081 JButton ok = new JButton();
1083 JButton loadColours = new JButton();
1085 JButton saveColours = new JButton();
1087 JPanel dasButtonPanel = new JPanel();
1089 JButton fetchDAS = new JButton();
1091 JButton saveDAS = new JButton();
1093 JButton cancelDAS = new JButton();
1095 JButton optimizeOrder = new JButton();
1097 JButton sortByScore = new JButton();
1099 JButton sortByDens = new JButton();
1101 JButton help = new JButton();
1103 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1105 private void jbInit() throws Exception
1107 this.setLayout(borderLayout1);
1108 settingsPane.setLayout(borderLayout2);
1109 dasSettingsPane.setLayout(borderLayout3);
1110 bigPanel.setLayout(borderLayout4);
1112 groupPanel = new JPanel();
1113 bigPanel.add(groupPanel, BorderLayout.NORTH);
1115 invert.setFont(JvSwingUtils.getLabelFont());
1116 invert.setText(MessageManager.getString("label.invert_selection"));
1117 invert.addActionListener(new ActionListener()
1120 public void actionPerformed(ActionEvent e)
1125 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1126 optimizeOrder.setText(MessageManager.getString("label.optimise_order"));
1127 optimizeOrder.addActionListener(new ActionListener()
1130 public void actionPerformed(ActionEvent e)
1135 sortByScore.setFont(JvSwingUtils.getLabelFont());
1137 .setText(MessageManager.getString("label.seq_sort_by_score"));
1138 sortByScore.addActionListener(new ActionListener()
1141 public void actionPerformed(ActionEvent e)
1143 af.avc.sortAlignmentByFeatureScore(null);
1146 sortByDens.setFont(JvSwingUtils.getLabelFont());
1147 sortByDens.setText(MessageManager
1148 .getString("label.sequence_sort_by_density"));
1149 sortByDens.addActionListener(new ActionListener()
1152 public void actionPerformed(ActionEvent e)
1154 af.avc.sortAlignmentByFeatureDensity(null);
1157 help.setFont(JvSwingUtils.getLabelFont());
1158 help.setText(MessageManager.getString("action.help"));
1159 help.addActionListener(new ActionListener()
1162 public void actionPerformed(ActionEvent e)
1166 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1167 } catch (HelpSetException e1)
1169 e1.printStackTrace();
1173 help.setFont(JvSwingUtils.getLabelFont());
1174 help.setText(MessageManager.getString("action.help"));
1175 help.addActionListener(new ActionListener()
1178 public void actionPerformed(ActionEvent e)
1182 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1183 } catch (HelpSetException e1)
1185 e1.printStackTrace();
1189 cancel.setFont(JvSwingUtils.getLabelFont());
1190 cancel.setText(MessageManager.getString("action.cancel"));
1191 cancel.addActionListener(new ActionListener()
1194 public void actionPerformed(ActionEvent e)
1196 fr.setTransparency(originalTransparency);
1197 updateFeatureRenderer(originalData);
1201 ok.setFont(JvSwingUtils.getLabelFont());
1202 ok.setText(MessageManager.getString("action.ok"));
1203 ok.addActionListener(new ActionListener()
1206 public void actionPerformed(ActionEvent e)
1211 loadColours.setFont(JvSwingUtils.getLabelFont());
1212 loadColours.setText(MessageManager.getString("label.load_colours"));
1213 loadColours.addActionListener(new ActionListener()
1216 public void actionPerformed(ActionEvent e)
1221 saveColours.setFont(JvSwingUtils.getLabelFont());
1222 saveColours.setText(MessageManager.getString("label.save_colours"));
1223 saveColours.addActionListener(new ActionListener()
1226 public void actionPerformed(ActionEvent e)
1231 transparency.addChangeListener(new ChangeListener()
1234 public void stateChanged(ChangeEvent evt)
1236 fr.setTransparency((100 - transparency.getValue()) / 100f);
1237 af.alignPanel.paintAlignment(true);
1241 transparency.setMaximum(70);
1242 transparency.setToolTipText(MessageManager
1243 .getString("label.transparency_tip"));
1244 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1245 fetchDAS.addActionListener(new ActionListener()
1248 public void actionPerformed(ActionEvent e)
1250 fetchDAS_actionPerformed(e);
1253 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1254 saveDAS.addActionListener(new ActionListener()
1257 public void actionPerformed(ActionEvent e)
1259 saveDAS_actionPerformed(e);
1262 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1263 dasSettingsPane.setBorder(null);
1264 cancelDAS.setEnabled(false);
1265 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1266 cancelDAS.addActionListener(new ActionListener()
1269 public void actionPerformed(ActionEvent e)
1271 cancelDAS_actionPerformed(e);
1274 this.add(tabbedPane, java.awt.BorderLayout.CENTER);
1275 tabbedPane.addTab(MessageManager.getString("label.feature_settings"),
1277 tabbedPane.addTab(MessageManager.getString("label.das_settings"),
1279 bigPanel.add(transPanel, java.awt.BorderLayout.SOUTH);
1280 transbuttons.add(optimizeOrder);
1281 transbuttons.add(invert);
1282 transbuttons.add(sortByScore);
1283 transbuttons.add(sortByDens);
1284 transbuttons.add(help);
1285 JPanel sliderPanel = new JPanel();
1286 sliderPanel.add(transparency);
1287 transPanel.add(transparency);
1288 transPanel.add(transbuttons);
1289 buttonPanel.add(ok);
1290 buttonPanel.add(cancel);
1291 buttonPanel.add(loadColours);
1292 buttonPanel.add(saveColours);
1293 bigPanel.add(scrollPane, java.awt.BorderLayout.CENTER);
1294 dasSettingsPane.add(dasButtonPanel, java.awt.BorderLayout.SOUTH);
1295 dasButtonPanel.add(fetchDAS);
1296 dasButtonPanel.add(cancelDAS);
1297 dasButtonPanel.add(saveDAS);
1298 settingsPane.add(bigPanel, java.awt.BorderLayout.CENTER);
1299 settingsPane.add(buttonPanel, java.awt.BorderLayout.SOUTH);
1302 public void fetchDAS_actionPerformed(ActionEvent e)
1304 fetchDAS.setEnabled(false);
1305 cancelDAS.setEnabled(true);
1306 dassourceBrowser.setGuiEnabled(false);
1307 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1308 .getSelectedSources();
1309 doDasFeatureFetch(selectedSources, true, true);
1313 * get the features from selectedSources for all or the current selection
1315 * @param selectedSources
1316 * @param checkDbRefs
1317 * @param promptFetchDbRefs
1319 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1320 boolean checkDbRefs, boolean promptFetchDbRefs)
1322 SequenceI[] dataset, seqs;
1324 AlignmentViewport vp = af.getViewport();
1325 if (vp.getSelectionGroup() != null
1326 && vp.getSelectionGroup().getSize() > 0)
1328 iSize = vp.getSelectionGroup().getSize();
1329 dataset = new SequenceI[iSize];
1330 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1334 iSize = vp.getAlignment().getHeight();
1335 seqs = vp.getAlignment().getSequencesArray();
1338 dataset = new SequenceI[iSize];
1339 for (int i = 0; i < iSize; i++)
1341 dataset[i] = seqs[i].getDatasetSequence();
1344 cancelDAS.setEnabled(true);
1345 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1346 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1347 af.getViewport().setShowSequenceFeatures(true);
1348 af.showSeqFeatures.setSelected(true);
1352 * blocking call to initialise the das source browser
1354 public void initDasSources()
1356 dassourceBrowser.initDasSources();
1360 * examine the current list of das sources and return any matching the given
1361 * nicknames in sources
1364 * Vector of Strings to resolve to DAS source nicknames.
1365 * @return sources that are present in source list.
1367 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1369 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1373 * get currently selected das sources. ensure you have called initDasSources
1374 * before calling this.
1376 * @return vector of selected das source nicknames
1378 public Vector<jalviewSourceI> getSelectedSources()
1380 return dassourceBrowser.getSelectedSources();
1384 * properly initialise DAS fetcher and then initiate a new thread to fetch
1385 * features from the named sources (rather than any turned on by default)
1389 * if true then runs in same thread, otherwise passes to the Swing
1392 public void fetchDasFeatures(Vector<String> sources, boolean block)
1395 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1396 .resolveSourceNicknames(sources);
1397 if (resolved.size() == 0)
1399 resolved = dassourceBrowser.getSelectedSources();
1401 if (resolved.size() > 0)
1403 final List<jalviewSourceI> dassources = resolved;
1404 fetchDAS.setEnabled(false);
1405 // cancelDAS.setEnabled(true); doDasFetch does this.
1406 Runnable fetcher = new Runnable()
1412 doDasFeatureFetch(dassources, true, false);
1422 SwingUtilities.invokeLater(fetcher);
1427 public void saveDAS_actionPerformed(ActionEvent e)
1430 .saveProperties(jalview.bin.Cache.applicationProperties);
1433 public void complete()
1435 fetchDAS.setEnabled(true);
1436 cancelDAS.setEnabled(false);
1437 dassourceBrowser.setGuiEnabled(true);
1441 public void cancelDAS_actionPerformed(ActionEvent e)
1443 if (dasFeatureFetcher != null)
1445 dasFeatureFetcher.cancel();
1450 public void noDasSourceActive()
1454 .showInternalConfirmDialog(
1457 .getString("label.no_das_sources_selected_warn"),
1459 .getString("label.no_das_sources_selected_title"),
1460 JvOptionPane.DEFAULT_OPTION,
1461 JvOptionPane.INFORMATION_MESSAGE);
1464 // ///////////////////////////////////////////////////////////////////////
1465 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1466 // ///////////////////////////////////////////////////////////////////////
1467 class FeatureTableModel extends AbstractTableModel
1469 FeatureTableModel(Object[][] data)
1474 private String[] columnNames = {
1475 MessageManager.getString("label.feature_type"),
1476 MessageManager.getString("action.colour"),
1477 MessageManager.getString("label.display") };
1479 private Object[][] data;
1481 public Object[][] getData()
1486 public void setData(Object[][] data)
1492 public int getColumnCount()
1494 return columnNames.length;
1497 public Object[] getRow(int row)
1503 public int getRowCount()
1509 public String getColumnName(int col)
1511 return columnNames[col];
1515 public Object getValueAt(int row, int col)
1517 return data[row][col];
1521 public Class getColumnClass(int c)
1523 return getValueAt(0, c).getClass();
1527 public boolean isCellEditable(int row, int col)
1529 return col == 0 ? false : true;
1533 public void setValueAt(Object value, int row, int col)
1535 data[row][col] = value;
1536 fireTableCellUpdated(row, col);
1537 updateFeatureRenderer(data);
1542 class ColorRenderer extends JLabel implements TableCellRenderer
1544 javax.swing.border.Border unselectedBorder = null;
1546 javax.swing.border.Border selectedBorder = null;
1548 final String baseTT = "Click to edit, right/apple click for menu.";
1550 public ColorRenderer()
1552 setOpaque(true); // MUST do this for background to show up.
1553 setHorizontalTextPosition(SwingConstants.CENTER);
1554 setVerticalTextPosition(SwingConstants.CENTER);
1558 public Component getTableCellRendererComponent(JTable tbl,
1559 Object color, boolean isSelected, boolean hasFocus, int row,
1562 FeatureColourI cellColour = (FeatureColourI) color;
1563 // JLabel comp = new JLabel();
1567 // setBounds(getBounds());
1569 setToolTipText(baseTT);
1570 setBackground(tbl.getBackground());
1571 if (!cellColour.isSimpleColour())
1573 Rectangle cr = tbl.getCellRect(row, column, false);
1574 FeatureSettings.renderGraduatedColor(this, cellColour,
1575 (int) cr.getWidth(), (int) cr.getHeight());
1582 newColor = cellColour.getColour();
1583 setBackground(newColor);
1587 if (selectedBorder == null)
1589 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1590 tbl.getSelectionBackground());
1592 setBorder(selectedBorder);
1596 if (unselectedBorder == null)
1598 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1599 tbl.getBackground());
1601 setBorder(unselectedBorder);
1609 * update comp using rendering settings from gcol
1614 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1616 int w = comp.getWidth(), h = comp.getHeight();
1619 w = (int) comp.getPreferredSize().getWidth();
1620 h = (int) comp.getPreferredSize().getHeight();
1627 renderGraduatedColor(comp, gcol, w, h);
1630 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1633 boolean thr = false;
1636 if (gcol.isAboveThreshold())
1640 tt += "Thresholded (Above " + gcol.getThreshold() + ") ";
1642 if (gcol.isBelowThreshold())
1646 tt += "Thresholded (Below " + gcol.getThreshold() + ") ";
1648 if (gcol.isColourByLabel())
1650 tt = "Coloured by label text. " + tt;
1660 Color newColor = gcol.getMaxColour();
1661 comp.setBackground(newColor);
1662 // System.err.println("Width is " + w / 2);
1663 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1664 comp.setIcon(ficon);
1665 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1666 // + newColor.getGreen() + ", " + newColor.getBlue()
1667 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1668 // + ", " + minCol.getBlue() + ")");
1670 comp.setHorizontalAlignment(SwingConstants.CENTER);
1672 if (tt.length() > 0)
1674 if (comp.getToolTipText() == null)
1676 comp.setToolTipText(tt);
1680 comp.setToolTipText(tt + " " + comp.getToolTipText());
1686 class FeatureIcon implements Icon
1688 FeatureColourI gcol;
1692 boolean midspace = false;
1694 int width = 50, height = 20;
1696 int s1, e1; // start and end of midpoint band for thresholded symbol
1698 Color mpcolour = Color.white;
1700 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1720 public int getIconWidth()
1726 public int getIconHeight()
1732 public void paintIcon(Component c, Graphics g, int x, int y)
1735 if (gcol.isColourByLabel())
1738 g.fillRect(0, 0, width, height);
1739 // need an icon here.
1740 g.setColor(gcol.getMaxColour());
1742 g.setFont(new Font("Verdana", Font.PLAIN, 9));
1744 // g.setFont(g.getFont().deriveFont(
1745 // AffineTransform.getScaleInstance(
1746 // width/g.getFontMetrics().stringWidth("Label"),
1747 // height/g.getFontMetrics().getHeight())));
1749 g.drawString(MessageManager.getString("label.label"), 0, 0);
1754 Color minCol = gcol.getMinColour();
1756 g.fillRect(0, 0, s1, height);
1759 g.setColor(Color.white);
1760 g.fillRect(s1, 0, e1 - s1, height);
1762 g.setColor(gcol.getMaxColour());
1763 g.fillRect(0, e1, width - e1, height);
1768 class ColorEditor extends AbstractCellEditor implements TableCellEditor,
1773 FeatureColourI currentColor;
1775 FeatureColourChooser chooser;
1781 JColorChooser colorChooser;
1785 protected static final String EDIT = "edit";
1787 int selectedRow = 0;
1789 public ColorEditor(FeatureSettings me)
1792 // Set up the editor (from the table's point of view),
1793 // which is a button.
1794 // This button brings up the color chooser dialog,
1795 // which is the editor from the user's point of view.
1796 button = new JButton();
1797 button.setActionCommand(EDIT);
1798 button.addActionListener(this);
1799 button.setBorderPainted(false);
1800 // Set up the dialog that the button brings up.
1801 colorChooser = new JColorChooser();
1802 dialog = JColorChooser.createDialog(button, "Select new Colour", true, // modal
1803 colorChooser, this, // OK button handler
1804 null); // no CANCEL button handler
1808 * Handles events from the editor button and from the dialog's OK button.
1811 public void actionPerformed(ActionEvent e)
1814 if (EDIT.equals(e.getActionCommand()))
1816 // The user has clicked the cell, so
1817 // bring up the dialog.
1818 if (currentColor.isSimpleColour())
1820 // bring up simple color chooser
1821 button.setBackground(currentColor.getColour());
1822 colorChooser.setColor(currentColor.getColour());
1823 dialog.setVisible(true);
1827 // bring up graduated chooser.
1828 chooser = new FeatureColourChooser(me.fr, type);
1829 chooser.setRequestFocusEnabled(true);
1830 chooser.requestFocus();
1831 chooser.addActionListener(this);
1833 // Make the renderer reappear.
1834 fireEditingStopped();
1838 { // User pressed dialog's "OK" button.
1839 if (currentColor.isSimpleColour())
1841 currentColor = new FeatureColour(colorChooser.getColor());
1845 currentColor = chooser.getLastColour();
1847 me.table.setValueAt(getCellEditorValue(), selectedRow, 1);
1848 fireEditingStopped();
1849 me.table.validate();
1853 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1855 public Object getCellEditorValue()
1857 return currentColor;
1860 // Implement the one method defined by TableCellEditor.
1862 public Component getTableCellEditorComponent(JTable table, Object value,
1863 boolean isSelected, int row, int column)
1865 currentColor = (FeatureColourI) value;
1866 this.selectedRow = row;
1867 type = me.table.getValueAt(row, 0).toString();
1868 button.setOpaque(true);
1869 button.setBackground(me.getBackground());
1870 if (!currentColor.isSimpleColour())
1872 JLabel btn = new JLabel();
1873 btn.setSize(button.getSize());
1874 FeatureSettings.renderGraduatedColor(btn, currentColor);
1875 button.setBackground(btn.getBackground());
1876 button.setIcon(btn.getIcon());
1877 button.setText(btn.getText());
1882 button.setIcon(null);
1883 button.setBackground(currentColor.getColour());