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;
135 public FeatureSettings(AlignFrame af)
138 fr = af.getFeatureRenderer();
139 // allow transparency to be recovered
140 transparency.setMaximum(100 - (int) ((originalTransparency = fr
141 .getTransparency()) * 100));
146 } catch (Exception ex)
148 ex.printStackTrace();
154 public String getToolTipText(MouseEvent e)
156 if (table.columnAtPoint(e.getPoint()) == 0)
159 * Tooltip for feature name only
161 return JvSwingUtils.wrapTooltip(true, MessageManager
162 .getString("label.feature_settings_click_drag"));
167 table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
168 table.setFont(new Font("Verdana", Font.PLAIN, 12));
169 table.setDefaultRenderer(Color.class, new ColorRenderer());
171 table.setDefaultEditor(Color.class, new ColorEditor(this));
173 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
174 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
175 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
177 table.addMouseListener(new MouseAdapter()
180 public void mousePressed(MouseEvent evt)
182 selectedRow = table.rowAtPoint(evt.getPoint());
183 if (evt.isPopupTrigger())
185 popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
186 table.getValueAt(selectedRow, 1), fr.getMinMax(),
187 evt.getX(), evt.getY());
189 else if (evt.getClickCount() == 2)
191 boolean invertSelection = evt.isAltDown();
192 boolean toggleSelection = Platform.isControlDown(evt);
193 boolean extendSelection = evt.isShiftDown();
194 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
195 invertSelection, extendSelection, toggleSelection,
196 (String) table.getValueAt(selectedRow, 0));
200 // isPopupTrigger fires on mouseReleased on Windows
202 public void mouseReleased(MouseEvent evt)
204 selectedRow = table.rowAtPoint(evt.getPoint());
205 if (evt.isPopupTrigger())
207 popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
208 table.getValueAt(selectedRow, 1), fr.getMinMax(),
209 evt.getX(), evt.getY());
214 table.addMouseMotionListener(new MouseMotionAdapter()
217 public void mouseDragged(MouseEvent evt)
219 int newRow = table.rowAtPoint(evt.getPoint());
220 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
223 * reposition 'selectedRow' to 'newRow' (the dragged to location)
224 * this could be more than one row away for a very fast drag action
225 * so just swap it with adjacent rows until we get it there
227 Object[][] data = ((FeatureTableModel) table.getModel())
229 int direction = newRow < selectedRow ? -1 : 1;
230 for (int i = selectedRow; i != newRow; i += direction)
232 Object[] temp = data[i];
233 data[i] = data[i + direction];
234 data[i + direction] = temp;
236 updateFeatureRenderer(data);
238 selectedRow = newRow;
242 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
243 // MessageManager.getString("label.feature_settings_click_drag")));
244 scrollPane.setViewportView(table);
246 dassourceBrowser = new DasSourceBrowser(this);
247 dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
249 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
251 fr.findAllFeatures(true); // display everything!
254 discoverAllFeatureData();
255 final PropertyChangeListener change;
256 final FeatureSettings fs = this;
257 fr.addPropertyChangeListener(change = new PropertyChangeListener()
260 public void propertyChange(PropertyChangeEvent evt)
262 if (!fs.resettingTable && !fs.handlingUpdate)
264 fs.handlingUpdate = true;
265 fs.resetTable(null); // new groups may be added with new seuqence
266 // feature types only
267 fs.handlingUpdate = false;
273 frame = new JInternalFrame();
274 frame.setContentPane(this);
275 if (Platform.isAMac())
277 Desktop.addInternalFrame(frame,
278 MessageManager.getString("label.sequence_feature_settings"),
283 Desktop.addInternalFrame(frame,
284 MessageManager.getString("label.sequence_feature_settings"),
287 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
289 frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
292 public void internalFrameClosed(
293 javax.swing.event.InternalFrameEvent evt)
295 fr.removePropertyChangeListener(change);
296 dassourceBrowser.fs = null;
299 frame.setLayer(JLayeredPane.PALETTE_LAYER);
302 protected void popupSort(final int selectedRow, final String type,
303 final Object typeCol, final Map<String, float[][]> minmax, int x,
306 final FeatureColourI featureColour = (FeatureColourI) typeCol;
308 JPopupMenu men = new JPopupMenu(MessageManager.formatMessage(
309 "label.settings_for_param", new String[] { type }));
310 JMenuItem scr = new JMenuItem(
311 MessageManager.getString("label.sort_by_score"));
313 final FeatureSettings me = this;
314 scr.addActionListener(new ActionListener()
318 public void actionPerformed(ActionEvent e)
320 me.af.avc.sortAlignmentByFeatureScore(Arrays
321 .asList(new String[] { type }));
325 JMenuItem dens = new JMenuItem(
326 MessageManager.getString("label.sort_by_density"));
327 dens.addActionListener(new ActionListener()
331 public void actionPerformed(ActionEvent e)
333 me.af.avc.sortAlignmentByFeatureDensity(Arrays
334 .asList(new String[] { type }));
341 final float[][] typeMinMax = minmax.get(type);
343 * final JCheckBoxMenuItem chb = new JCheckBoxMenuItem("Vary Height"); //
344 * this is broken at the moment and isn't that useful anyway!
345 * chb.setSelected(minmax.get(type) != null); chb.addActionListener(new
348 * public void actionPerformed(ActionEvent e) {
349 * chb.setState(chb.getState()); if (chb.getState()) { minmax.put(type,
350 * null); } else { minmax.put(type, typeMinMax); } }
356 if (typeMinMax != null && typeMinMax[0] != null)
358 // if (table.getValueAt(row, column));
359 // graduated colourschemes for those where minmax exists for the
360 // positional features
361 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
363 mxcol.setSelected(!featureColour.isSimpleColour());
365 mxcol.addActionListener(new ActionListener()
367 JColorChooser colorChooser;
370 public void actionPerformed(ActionEvent e)
372 if (e.getSource() == mxcol)
374 if (featureColour.isSimpleColour())
376 FeatureColourChooser fc = new FeatureColourChooser(me.fr,
378 fc.addActionListener(this);
382 // bring up simple color chooser
383 colorChooser = new JColorChooser();
384 JDialog dialog = JColorChooser.createDialog(me,
385 "Select new Colour", true, // modal
386 colorChooser, this, // OK button handler
387 null); // no CANCEL button handler
388 colorChooser.setColor(featureColour.getMaxColour());
389 dialog.setVisible(true);
394 if (e.getSource() instanceof FeatureColourChooser)
396 FeatureColourChooser fc = (FeatureColourChooser) e
398 table.setValueAt(fc.getLastColour(), selectedRow, 1);
403 // probably the color chooser!
405 new FeatureColour(colorChooser.getColor()),
408 me.updateFeatureRenderer(
409 ((FeatureTableModel) table.getModel()).getData(),
418 JMenuItem selCols = new JMenuItem(
419 MessageManager.getString("label.select_columns_containing"));
420 selCols.addActionListener(new ActionListener()
423 public void actionPerformed(ActionEvent arg0)
425 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
429 JMenuItem clearCols = new JMenuItem(
430 MessageManager.getString("label.select_columns_not_containing"));
431 clearCols.addActionListener(new ActionListener()
434 public void actionPerformed(ActionEvent arg0)
436 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
440 JMenuItem hideCols = new JMenuItem(
441 MessageManager.getString("label.hide_columns_containing"));
442 hideCols.addActionListener(new ActionListener()
445 public void actionPerformed(ActionEvent arg0)
447 fr.ap.alignFrame.hideFeatureColumns(type, true);
450 JMenuItem hideOtherCols = new JMenuItem(
451 MessageManager.getString("label.hide_columns_not_containing"));
452 hideOtherCols.addActionListener(new ActionListener()
455 public void actionPerformed(ActionEvent arg0)
457 fr.ap.alignFrame.hideFeatureColumns(type, false);
463 men.add(hideOtherCols);
464 men.show(table, x, y);
468 * true when Feature Settings are updating from feature renderer
470 private boolean handlingUpdate = false;
473 * holds {featureCount, totalExtent} for each feature type
475 Map<String, float[]> typeWidth = null;
478 synchronized public void discoverAllFeatureData()
480 Set<String> allGroups = new HashSet<String>();
481 AlignmentI alignment = af.getViewport().getAlignment();
483 for (int i = 0; i < alignment.getHeight(); i++)
485 SequenceI seq = alignment.getSequenceAt(i);
486 for (String group : seq.getFeatures().getFeatureGroups(true))
488 if (group != null && !allGroups.contains(group))
490 allGroups.add(group);
491 checkGroupState(group);
502 * Synchronise gui group list and check visibility of group
505 * @return true if group is visible
507 private boolean checkGroupState(String group)
509 boolean visible = fr.checkGroupVisibility(group, true);
511 if (groupPanel == null)
513 groupPanel = new JPanel();
516 boolean alreadyAdded = false;
517 for (int g = 0; g < groupPanel.getComponentCount(); g++)
519 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
522 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
532 final String grp = group;
533 final JCheckBox check = new JCheckBox(group, visible);
534 check.setFont(new Font("Serif", Font.BOLD, 12));
535 check.addItemListener(new ItemListener()
538 public void itemStateChanged(ItemEvent evt)
540 fr.setGroupVisibility(check.getText(), check.isSelected());
541 af.alignPanel.getSeqPanel().seqCanvas.repaint();
542 if (af.alignPanel.overviewPanel != null)
544 af.alignPanel.overviewPanel.updateOverviewImage();
547 resetTable(new String[] { grp });
550 groupPanel.add(check);
554 boolean resettingTable = false;
556 synchronized void resetTable(String[] groupChanged)
562 resettingTable = true;
563 typeWidth = new Hashtable<String, float[]>();
564 // TODO: change avWidth calculation to 'per-sequence' average and use long
567 Set<String> displayableTypes = new HashSet<String>();
570 * determine which feature types may be visible depending on
571 * which groups are selected, and recompute average width data
573 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
576 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
579 * get the sequence's groups for positional features
580 * and keep track of which groups are visible
582 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
583 Set<String> visibleGroups = new HashSet<String>();
584 for (String group : groups)
586 if (group == null || checkGroupState(group))
588 visibleGroups.add(group);
593 * get distinct feature types for visible groups
594 * record distinct visible types, and their count and total length
596 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
597 visibleGroups.toArray(new String[visibleGroups.size()]));
598 for (String type : types)
600 displayableTypes.add(type);
601 float[] avWidth = typeWidth.get(type);
604 avWidth = new float[2];
605 typeWidth.put(type, avWidth);
607 // todo this could include features with a non-visible group
608 // - do we greatly care?
609 // todo should we include non-displayable features here, and only
610 // update when features are added?
611 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
612 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
616 Object[][] data = new Object[displayableTypes.size()][3];
619 if (fr.hasRenderOrder())
623 fr.findAllFeatures(groupChanged != null); // prod to update
624 // colourschemes. but don't
626 // First add the checks in the previous render order,
627 // in case the window has been closed and reopened
629 List<String> frl = fr.getRenderOrder();
630 for (int ro = frl.size() - 1; ro > -1; ro--)
632 String type = frl.get(ro);
634 if (!displayableTypes.contains(type))
639 data[dataIndex][0] = type;
640 data[dataIndex][1] = fr.getFeatureStyle(type);
641 data[dataIndex][2] = new Boolean(af.getViewport()
642 .getFeaturesDisplayed().isVisible(type));
644 displayableTypes.remove(type);
649 * process any extra features belonging only to
650 * a group which was just selected
652 while (!displayableTypes.isEmpty())
654 String type = displayableTypes.iterator().next();
655 data[dataIndex][0] = type;
657 data[dataIndex][1] = fr.getFeatureStyle(type);
658 if (data[dataIndex][1] == null)
660 // "Colour has been updated in another view!!"
661 fr.clearRenderOrder();
665 data[dataIndex][2] = new Boolean(true);
667 displayableTypes.remove(type);
670 if (originalData == null)
672 originalData = new Object[data.length][3];
673 for (int i = 0; i < data.length; i++)
675 System.arraycopy(data[i], 0, originalData[i], 0, 3);
679 table.setModel(new FeatureTableModel(data));
680 table.getColumnModel().getColumn(0).setPreferredWidth(200);
682 if (groupPanel != null)
684 groupPanel.setLayout(new GridLayout(
685 fr.getFeatureGroupsSize() / 4 + 1, 4));
687 groupPanel.validate();
688 bigPanel.add(groupPanel, BorderLayout.NORTH);
691 updateFeatureRenderer(data, groupChanged != null);
692 resettingTable = false;
696 * reorder data based on the featureRenderers global priority list.
700 private void ensureOrder(Object[][] data)
702 boolean sort = false;
703 float[] order = new float[data.length];
704 for (int i = 0; i < order.length; i++)
706 order[i] = fr.getOrder(data[i][0].toString());
709 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
713 sort = sort || order[i - 1] > order[i];
718 jalview.util.QuickSort.sort(order, data);
724 JalviewFileChooser chooser = new JalviewFileChooser("fc",
725 "Sequence Feature Colours");
726 chooser.setFileView(new JalviewFileView());
727 chooser.setDialogTitle(MessageManager
728 .getString("label.load_feature_colours"));
729 chooser.setToolTipText(MessageManager.getString("action.load"));
731 int value = chooser.showOpenDialog(this);
733 if (value == JalviewFileChooser.APPROVE_OPTION)
735 File file = chooser.getSelectedFile();
739 InputStreamReader in = new InputStreamReader(new FileInputStream(
742 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
744 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
747 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
750 Color mincol = null, maxcol = null;
753 mincol = new Color(Integer.parseInt(newcol.getMinRGB(), 16));
754 maxcol = new Color(Integer.parseInt(newcol.getRGB(), 16));
756 } catch (Exception e)
758 Cache.log.warn("Couldn't parse out graduated feature color.",
761 FeatureColourI gcol = new FeatureColour(mincol, maxcol,
762 newcol.getMin(), newcol.getMax());
763 if (newcol.hasAutoScale())
765 gcol.setAutoScaled(newcol.getAutoScale());
767 if (newcol.hasColourByLabel())
769 gcol.setColourByLabel(newcol.getColourByLabel());
771 if (newcol.hasThreshold())
773 gcol.setThreshold(newcol.getThreshold());
775 if (newcol.getThreshType().length() > 0)
777 String ttyp = newcol.getThreshType();
778 if (ttyp.equalsIgnoreCase("ABOVE"))
780 gcol.setAboveThreshold(true);
782 if (ttyp.equalsIgnoreCase("BELOW"))
784 gcol.setBelowThreshold(true);
787 fr.setColour(name = newcol.getName(), gcol);
791 Color color = new Color(
792 Integer.parseInt(jucs.getColour(i).getRGB(), 16));
793 fr.setColour(name = jucs.getColour(i).getName(),
794 new FeatureColour(color));
796 fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
801 Object[][] data = ((FeatureTableModel) table.getModel())
804 updateFeatureRenderer(data, false);
807 } catch (Exception ex)
809 System.out.println("Error loading User Colour File\n" + ex);
816 JalviewFileChooser chooser = new JalviewFileChooser("fc",
817 "Sequence Feature Colours");
818 chooser.setFileView(new JalviewFileView());
819 chooser.setDialogTitle(MessageManager
820 .getString("label.save_feature_colours"));
821 chooser.setToolTipText(MessageManager.getString("action.save"));
823 int value = chooser.showSaveDialog(this);
825 if (value == JalviewFileChooser.APPROVE_OPTION)
827 String choice = chooser.getSelectedFile().getPath();
828 jalview.schemabinding.version2.JalviewUserColours ucs = new jalview.schemabinding.version2.JalviewUserColours();
829 ucs.setSchemeName("Sequence Features");
832 PrintWriter out = new PrintWriter(new OutputStreamWriter(
833 new FileOutputStream(choice), "UTF-8"));
835 Set<String> fr_colours = fr.getAllFeatureColours();
836 Iterator<String> e = fr_colours.iterator();
837 float[] sortOrder = new float[fr_colours.size()];
838 String[] sortTypes = new String[fr_colours.size()];
842 sortTypes[i] = e.next();
843 sortOrder[i] = fr.getOrder(sortTypes[i]);
846 QuickSort.sort(sortOrder, sortTypes);
848 for (i = 0; i < sortTypes.length; i++)
850 jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
851 col.setName(sortTypes[i]);
852 FeatureColourI fcol = fr.getFeatureStyle(sortTypes[i]);
853 if (fcol.isSimpleColour())
855 col.setRGB(Format.getHexString(fcol.getColour()));
859 col.setRGB(Format.getHexString(fcol.getMaxColour()));
860 col.setMin(fcol.getMin());
861 col.setMax(fcol.getMax());
862 col.setMinRGB(jalview.util.Format.getHexString(fcol
864 col.setAutoScale(fcol.isAutoScaled());
865 col.setThreshold(fcol.getThreshold());
866 col.setColourByLabel(fcol.isColourByLabel());
867 col.setThreshType(fcol.isAboveThreshold() ? "ABOVE" : (fcol
868 .isBelowThreshold() ? "BELOW" : "NONE"));
874 } catch (Exception ex)
876 ex.printStackTrace();
881 public void invertSelection()
883 for (int i = 0; i < table.getRowCount(); i++)
885 Boolean value = (Boolean) table.getValueAt(i, 2);
887 table.setValueAt(new Boolean(!value.booleanValue()), i, 2);
891 public void orderByAvWidth()
893 if (table == null || table.getModel() == null)
897 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
898 float[] width = new float[data.length];
902 for (int i = 0; i < data.length; i++)
904 awidth = typeWidth.get(data[i][0]);
907 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
908 // weight - but have to make per
909 // sequence, too (awidth[2])
910 // if (width[i]==1) // hack to distinguish single width sequences.
922 boolean sort = false;
923 for (int i = 0; i < width.length; i++)
925 // awidth = (float[]) typeWidth.get(data[i][0]);
928 width[i] = fr.getOrder(data[i][0].toString());
931 width[i] = fr.setOrder(data[i][0].toString(), i / data.length);
936 width[i] /= max; // normalize
937 fr.setOrder(data[i][0].toString(), width[i]); // store for later
941 sort = sort || width[i - 1] > width[i];
946 jalview.util.QuickSort.sort(width, data);
947 // update global priority order
950 updateFeatureRenderer(data, false);
958 frame.setClosed(true);
959 } catch (Exception exe)
965 public void updateFeatureRenderer(Object[][] data)
967 updateFeatureRenderer(data, true);
971 * Update the priority order of features; only repaint if this changed the
972 * order of visible features
977 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
979 if (fr.setFeaturePriority(data, visibleNew))
981 af.alignPanel.paintAlignment(true);
985 int selectedRow = -1;
987 JTabbedPane tabbedPane = new JTabbedPane();
989 BorderLayout borderLayout1 = new BorderLayout();
991 BorderLayout borderLayout2 = new BorderLayout();
993 BorderLayout borderLayout3 = new BorderLayout();
995 JPanel bigPanel = new JPanel();
997 BorderLayout borderLayout4 = new BorderLayout();
999 JButton invert = new JButton();
1001 JPanel buttonPanel = new JPanel();
1003 JButton cancel = new JButton();
1005 JButton ok = new JButton();
1007 JButton loadColours = new JButton();
1009 JButton saveColours = new JButton();
1011 JPanel dasButtonPanel = new JPanel();
1013 JButton fetchDAS = new JButton();
1015 JButton saveDAS = new JButton();
1017 JButton cancelDAS = new JButton();
1019 JButton optimizeOrder = new JButton();
1021 JButton sortByScore = new JButton();
1023 JButton sortByDens = new JButton();
1025 JButton help = new JButton();
1027 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1029 private void jbInit() throws Exception
1031 this.setLayout(borderLayout1);
1032 settingsPane.setLayout(borderLayout2);
1033 dasSettingsPane.setLayout(borderLayout3);
1034 bigPanel.setLayout(borderLayout4);
1035 invert.setFont(JvSwingUtils.getLabelFont());
1036 invert.setText(MessageManager.getString("label.invert_selection"));
1037 invert.addActionListener(new ActionListener()
1040 public void actionPerformed(ActionEvent e)
1045 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1046 optimizeOrder.setText(MessageManager.getString("label.optimise_order"));
1047 optimizeOrder.addActionListener(new ActionListener()
1050 public void actionPerformed(ActionEvent e)
1055 sortByScore.setFont(JvSwingUtils.getLabelFont());
1057 .setText(MessageManager.getString("label.seq_sort_by_score"));
1058 sortByScore.addActionListener(new ActionListener()
1061 public void actionPerformed(ActionEvent e)
1063 af.avc.sortAlignmentByFeatureScore(null);
1066 sortByDens.setFont(JvSwingUtils.getLabelFont());
1067 sortByDens.setText(MessageManager
1068 .getString("label.sequence_sort_by_density"));
1069 sortByDens.addActionListener(new ActionListener()
1072 public void actionPerformed(ActionEvent e)
1074 af.avc.sortAlignmentByFeatureDensity(null);
1077 help.setFont(JvSwingUtils.getLabelFont());
1078 help.setText(MessageManager.getString("action.help"));
1079 help.addActionListener(new ActionListener()
1082 public void actionPerformed(ActionEvent e)
1086 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1087 } catch (HelpSetException e1)
1089 e1.printStackTrace();
1093 help.setFont(JvSwingUtils.getLabelFont());
1094 help.setText(MessageManager.getString("action.help"));
1095 help.addActionListener(new ActionListener()
1098 public void actionPerformed(ActionEvent e)
1102 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1103 } catch (HelpSetException e1)
1105 e1.printStackTrace();
1109 cancel.setFont(JvSwingUtils.getLabelFont());
1110 cancel.setText(MessageManager.getString("action.cancel"));
1111 cancel.addActionListener(new ActionListener()
1114 public void actionPerformed(ActionEvent e)
1116 fr.setTransparency(originalTransparency);
1117 updateFeatureRenderer(originalData);
1121 ok.setFont(JvSwingUtils.getLabelFont());
1122 ok.setText(MessageManager.getString("action.ok"));
1123 ok.addActionListener(new ActionListener()
1126 public void actionPerformed(ActionEvent e)
1131 loadColours.setFont(JvSwingUtils.getLabelFont());
1132 loadColours.setText(MessageManager.getString("label.load_colours"));
1133 loadColours.addActionListener(new ActionListener()
1136 public void actionPerformed(ActionEvent e)
1141 saveColours.setFont(JvSwingUtils.getLabelFont());
1142 saveColours.setText(MessageManager.getString("label.save_colours"));
1143 saveColours.addActionListener(new ActionListener()
1146 public void actionPerformed(ActionEvent e)
1151 transparency.addChangeListener(new ChangeListener()
1154 public void stateChanged(ChangeEvent evt)
1156 fr.setTransparency((100 - transparency.getValue()) / 100f);
1157 af.alignPanel.paintAlignment(true);
1161 transparency.setMaximum(70);
1162 transparency.setToolTipText(MessageManager
1163 .getString("label.transparency_tip"));
1164 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1165 fetchDAS.addActionListener(new ActionListener()
1168 public void actionPerformed(ActionEvent e)
1170 fetchDAS_actionPerformed(e);
1173 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1174 saveDAS.addActionListener(new ActionListener()
1177 public void actionPerformed(ActionEvent e)
1179 saveDAS_actionPerformed(e);
1182 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1183 dasSettingsPane.setBorder(null);
1184 cancelDAS.setEnabled(false);
1185 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1186 cancelDAS.addActionListener(new ActionListener()
1189 public void actionPerformed(ActionEvent e)
1191 cancelDAS_actionPerformed(e);
1194 this.add(tabbedPane, java.awt.BorderLayout.CENTER);
1195 tabbedPane.addTab(MessageManager.getString("label.feature_settings"),
1197 tabbedPane.addTab(MessageManager.getString("label.das_settings"),
1199 bigPanel.add(transPanel, java.awt.BorderLayout.SOUTH);
1200 transbuttons.add(optimizeOrder);
1201 transbuttons.add(invert);
1202 transbuttons.add(sortByScore);
1203 transbuttons.add(sortByDens);
1204 transbuttons.add(help);
1205 JPanel sliderPanel = new JPanel();
1206 sliderPanel.add(transparency);
1207 transPanel.add(transparency);
1208 transPanel.add(transbuttons);
1209 buttonPanel.add(ok);
1210 buttonPanel.add(cancel);
1211 buttonPanel.add(loadColours);
1212 buttonPanel.add(saveColours);
1213 bigPanel.add(scrollPane, java.awt.BorderLayout.CENTER);
1214 dasSettingsPane.add(dasButtonPanel, java.awt.BorderLayout.SOUTH);
1215 dasButtonPanel.add(fetchDAS);
1216 dasButtonPanel.add(cancelDAS);
1217 dasButtonPanel.add(saveDAS);
1218 settingsPane.add(bigPanel, java.awt.BorderLayout.CENTER);
1219 settingsPane.add(buttonPanel, java.awt.BorderLayout.SOUTH);
1222 public void fetchDAS_actionPerformed(ActionEvent e)
1224 fetchDAS.setEnabled(false);
1225 cancelDAS.setEnabled(true);
1226 dassourceBrowser.setGuiEnabled(false);
1227 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1228 .getSelectedSources();
1229 doDasFeatureFetch(selectedSources, true, true);
1233 * get the features from selectedSources for all or the current selection
1235 * @param selectedSources
1236 * @param checkDbRefs
1237 * @param promptFetchDbRefs
1239 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1240 boolean checkDbRefs, boolean promptFetchDbRefs)
1242 SequenceI[] dataset, seqs;
1244 AlignmentViewport vp = af.getViewport();
1245 if (vp.getSelectionGroup() != null
1246 && vp.getSelectionGroup().getSize() > 0)
1248 iSize = vp.getSelectionGroup().getSize();
1249 dataset = new SequenceI[iSize];
1250 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1254 iSize = vp.getAlignment().getHeight();
1255 seqs = vp.getAlignment().getSequencesArray();
1258 dataset = new SequenceI[iSize];
1259 for (int i = 0; i < iSize; i++)
1261 dataset[i] = seqs[i].getDatasetSequence();
1264 cancelDAS.setEnabled(true);
1265 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1266 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1267 af.getViewport().setShowSequenceFeatures(true);
1268 af.showSeqFeatures.setSelected(true);
1272 * blocking call to initialise the das source browser
1274 public void initDasSources()
1276 dassourceBrowser.initDasSources();
1280 * examine the current list of das sources and return any matching the given
1281 * nicknames in sources
1284 * Vector of Strings to resolve to DAS source nicknames.
1285 * @return sources that are present in source list.
1287 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1289 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1293 * get currently selected das sources. ensure you have called initDasSources
1294 * before calling this.
1296 * @return vector of selected das source nicknames
1298 public Vector<jalviewSourceI> getSelectedSources()
1300 return dassourceBrowser.getSelectedSources();
1304 * properly initialise DAS fetcher and then initiate a new thread to fetch
1305 * features from the named sources (rather than any turned on by default)
1309 * if true then runs in same thread, otherwise passes to the Swing
1312 public void fetchDasFeatures(Vector<String> sources, boolean block)
1315 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1316 .resolveSourceNicknames(sources);
1317 if (resolved.size() == 0)
1319 resolved = dassourceBrowser.getSelectedSources();
1321 if (resolved.size() > 0)
1323 final List<jalviewSourceI> dassources = resolved;
1324 fetchDAS.setEnabled(false);
1325 // cancelDAS.setEnabled(true); doDasFetch does this.
1326 Runnable fetcher = new Runnable()
1332 doDasFeatureFetch(dassources, true, false);
1342 SwingUtilities.invokeLater(fetcher);
1347 public void saveDAS_actionPerformed(ActionEvent e)
1350 .saveProperties(jalview.bin.Cache.applicationProperties);
1353 public void complete()
1355 fetchDAS.setEnabled(true);
1356 cancelDAS.setEnabled(false);
1357 dassourceBrowser.setGuiEnabled(true);
1361 public void cancelDAS_actionPerformed(ActionEvent e)
1363 if (dasFeatureFetcher != null)
1365 dasFeatureFetcher.cancel();
1370 public void noDasSourceActive()
1374 .showInternalConfirmDialog(
1377 .getString("label.no_das_sources_selected_warn"),
1379 .getString("label.no_das_sources_selected_title"),
1380 JvOptionPane.DEFAULT_OPTION,
1381 JvOptionPane.INFORMATION_MESSAGE);
1384 // ///////////////////////////////////////////////////////////////////////
1385 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1386 // ///////////////////////////////////////////////////////////////////////
1387 class FeatureTableModel extends AbstractTableModel
1389 FeatureTableModel(Object[][] data)
1394 private String[] columnNames = {
1395 MessageManager.getString("label.feature_type"),
1396 MessageManager.getString("action.colour"),
1397 MessageManager.getString("label.display") };
1399 private Object[][] data;
1401 public Object[][] getData()
1406 public void setData(Object[][] data)
1412 public int getColumnCount()
1414 return columnNames.length;
1417 public Object[] getRow(int row)
1423 public int getRowCount()
1429 public String getColumnName(int col)
1431 return columnNames[col];
1435 public Object getValueAt(int row, int col)
1437 return data[row][col];
1441 public Class getColumnClass(int c)
1443 return getValueAt(0, c).getClass();
1447 public boolean isCellEditable(int row, int col)
1449 return col == 0 ? false : true;
1453 public void setValueAt(Object value, int row, int col)
1455 data[row][col] = value;
1456 fireTableCellUpdated(row, col);
1457 updateFeatureRenderer(data);
1462 class ColorRenderer extends JLabel implements TableCellRenderer
1464 javax.swing.border.Border unselectedBorder = null;
1466 javax.swing.border.Border selectedBorder = null;
1468 final String baseTT = "Click to edit, right/apple click for menu.";
1470 public ColorRenderer()
1472 setOpaque(true); // MUST do this for background to show up.
1473 setHorizontalTextPosition(SwingConstants.CENTER);
1474 setVerticalTextPosition(SwingConstants.CENTER);
1478 public Component getTableCellRendererComponent(JTable tbl,
1479 Object color, boolean isSelected, boolean hasFocus, int row,
1482 FeatureColourI cellColour = (FeatureColourI) color;
1483 // JLabel comp = new JLabel();
1487 // setBounds(getBounds());
1489 setToolTipText(baseTT);
1490 setBackground(tbl.getBackground());
1491 if (!cellColour.isSimpleColour())
1493 Rectangle cr = tbl.getCellRect(row, column, false);
1494 FeatureSettings.renderGraduatedColor(this, cellColour,
1495 (int) cr.getWidth(), (int) cr.getHeight());
1502 newColor = cellColour.getColour();
1503 setBackground(newColor);
1507 if (selectedBorder == null)
1509 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1510 tbl.getSelectionBackground());
1512 setBorder(selectedBorder);
1516 if (unselectedBorder == null)
1518 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1519 tbl.getBackground());
1521 setBorder(unselectedBorder);
1529 * update comp using rendering settings from gcol
1534 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1536 int w = comp.getWidth(), h = comp.getHeight();
1539 w = (int) comp.getPreferredSize().getWidth();
1540 h = (int) comp.getPreferredSize().getHeight();
1547 renderGraduatedColor(comp, gcol, w, h);
1550 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1553 boolean thr = false;
1556 if (gcol.isAboveThreshold())
1560 tt += "Thresholded (Above " + gcol.getThreshold() + ") ";
1562 if (gcol.isBelowThreshold())
1566 tt += "Thresholded (Below " + gcol.getThreshold() + ") ";
1568 if (gcol.isColourByLabel())
1570 tt = "Coloured by label text. " + tt;
1580 Color newColor = gcol.getMaxColour();
1581 comp.setBackground(newColor);
1582 // System.err.println("Width is " + w / 2);
1583 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1584 comp.setIcon(ficon);
1585 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1586 // + newColor.getGreen() + ", " + newColor.getBlue()
1587 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1588 // + ", " + minCol.getBlue() + ")");
1590 comp.setHorizontalAlignment(SwingConstants.CENTER);
1592 if (tt.length() > 0)
1594 if (comp.getToolTipText() == null)
1596 comp.setToolTipText(tt);
1600 comp.setToolTipText(tt + " " + comp.getToolTipText());
1606 class FeatureIcon implements Icon
1608 FeatureColourI gcol;
1612 boolean midspace = false;
1614 int width = 50, height = 20;
1616 int s1, e1; // start and end of midpoint band for thresholded symbol
1618 Color mpcolour = Color.white;
1620 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1640 public int getIconWidth()
1646 public int getIconHeight()
1652 public void paintIcon(Component c, Graphics g, int x, int y)
1655 if (gcol.isColourByLabel())
1658 g.fillRect(0, 0, width, height);
1659 // need an icon here.
1660 g.setColor(gcol.getMaxColour());
1662 g.setFont(new Font("Verdana", Font.PLAIN, 9));
1664 // g.setFont(g.getFont().deriveFont(
1665 // AffineTransform.getScaleInstance(
1666 // width/g.getFontMetrics().stringWidth("Label"),
1667 // height/g.getFontMetrics().getHeight())));
1669 g.drawString(MessageManager.getString("label.label"), 0, 0);
1674 Color minCol = gcol.getMinColour();
1676 g.fillRect(0, 0, s1, height);
1679 g.setColor(Color.white);
1680 g.fillRect(s1, 0, e1 - s1, height);
1682 g.setColor(gcol.getMaxColour());
1683 g.fillRect(0, e1, width - e1, height);
1688 class ColorEditor extends AbstractCellEditor implements TableCellEditor,
1693 FeatureColourI currentColor;
1695 FeatureColourChooser chooser;
1701 JColorChooser colorChooser;
1705 protected static final String EDIT = "edit";
1707 int selectedRow = 0;
1709 public ColorEditor(FeatureSettings me)
1712 // Set up the editor (from the table's point of view),
1713 // which is a button.
1714 // This button brings up the color chooser dialog,
1715 // which is the editor from the user's point of view.
1716 button = new JButton();
1717 button.setActionCommand(EDIT);
1718 button.addActionListener(this);
1719 button.setBorderPainted(false);
1720 // Set up the dialog that the button brings up.
1721 colorChooser = new JColorChooser();
1722 dialog = JColorChooser.createDialog(button, "Select new Colour", true, // modal
1723 colorChooser, this, // OK button handler
1724 null); // no CANCEL button handler
1728 * Handles events from the editor button and from the dialog's OK button.
1731 public void actionPerformed(ActionEvent e)
1734 if (EDIT.equals(e.getActionCommand()))
1736 // The user has clicked the cell, so
1737 // bring up the dialog.
1738 if (currentColor.isSimpleColour())
1740 // bring up simple color chooser
1741 button.setBackground(currentColor.getColour());
1742 colorChooser.setColor(currentColor.getColour());
1743 dialog.setVisible(true);
1747 // bring up graduated chooser.
1748 chooser = new FeatureColourChooser(me.fr, type);
1749 chooser.setRequestFocusEnabled(true);
1750 chooser.requestFocus();
1751 chooser.addActionListener(this);
1753 // Make the renderer reappear.
1754 fireEditingStopped();
1758 { // User pressed dialog's "OK" button.
1759 if (currentColor.isSimpleColour())
1761 currentColor = new FeatureColour(colorChooser.getColor());
1765 currentColor = chooser.getLastColour();
1767 me.table.setValueAt(getCellEditorValue(), selectedRow, 1);
1768 fireEditingStopped();
1769 me.table.validate();
1773 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1775 public Object getCellEditorValue()
1777 return currentColor;
1780 // Implement the one method defined by TableCellEditor.
1782 public Component getTableCellEditorComponent(JTable table, Object value,
1783 boolean isSelected, int row, int column)
1785 currentColor = (FeatureColourI) value;
1786 this.selectedRow = row;
1787 type = me.table.getValueAt(row, 0).toString();
1788 button.setOpaque(true);
1789 button.setBackground(me.getBackground());
1790 if (!currentColor.isSimpleColour())
1792 JLabel btn = new JLabel();
1793 btn.setSize(button.getSize());
1794 FeatureSettings.renderGraduatedColor(btn, currentColor);
1795 button.setBackground(btn.getBackground());
1796 button.setIcon(btn.getIcon());
1797 button.setText(btn.getText());
1802 button.setIcon(null);
1803 button.setBackground(currentColor.getColour());