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.addItemListener(new ItemListener()
534 public void itemStateChanged(ItemEvent evt)
536 fr.setGroupVisibility(check.getText(), check.isSelected());
537 af.alignPanel.getSeqPanel().seqCanvas.repaint();
538 if (af.alignPanel.overviewPanel != null)
540 af.alignPanel.overviewPanel.updateOverviewImage();
543 resetTable(new String[] { grp });
546 groupPanel.add(check);
550 boolean resettingTable = false;
552 synchronized void resetTable(String[] groupChanged)
558 resettingTable = true;
559 typeWidth = new Hashtable<String, float[]>();
560 // TODO: change avWidth calculation to 'per-sequence' average and use long
563 Set<String> displayableTypes = new HashSet<String>();
564 Set<String> foundGroups = new HashSet<String>();
567 * determine which feature types may be visible depending on
568 * which groups are selected, and recompute average width data
570 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
573 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
576 * get the sequence's groups for positional features
577 * and keep track of which groups are visible
579 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
580 Set<String> visibleGroups = new HashSet<String>();
581 for (String group : groups)
583 if (group == null || checkGroupState(group))
585 visibleGroups.add(group);
588 foundGroups.addAll(groups);
591 * get distinct feature types for visible groups
592 * record distinct visible types, and their count and total length
594 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
595 visibleGroups.toArray(new String[visibleGroups.size()]));
596 for (String type : types)
598 displayableTypes.add(type);
599 float[] avWidth = typeWidth.get(type);
602 avWidth = new float[2];
603 typeWidth.put(type, avWidth);
605 // todo this could include features with a non-visible group
606 // - do we greatly care?
607 // todo should we include non-displayable features here, and only
608 // update when features are added?
609 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
610 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
614 Object[][] data = new Object[displayableTypes.size()][3];
617 if (fr.hasRenderOrder())
621 fr.findAllFeatures(groupChanged != null); // prod to update
622 // colourschemes. but don't
624 // First add the checks in the previous render order,
625 // in case the window has been closed and reopened
627 List<String> frl = fr.getRenderOrder();
628 for (int ro = frl.size() - 1; ro > -1; ro--)
630 String type = frl.get(ro);
632 if (!displayableTypes.contains(type))
637 data[dataIndex][0] = type;
638 data[dataIndex][1] = fr.getFeatureStyle(type);
639 data[dataIndex][2] = new Boolean(
640 af.getViewport().getFeaturesDisplayed().isVisible(type));
642 displayableTypes.remove(type);
647 * process any extra features belonging only to
648 * a group which was just selected
650 while (!displayableTypes.isEmpty())
652 String type = displayableTypes.iterator().next();
653 data[dataIndex][0] = type;
655 data[dataIndex][1] = fr.getFeatureStyle(type);
656 if (data[dataIndex][1] == null)
658 // "Colour has been updated in another view!!"
659 fr.clearRenderOrder();
663 data[dataIndex][2] = new Boolean(true);
665 displayableTypes.remove(type);
668 if (originalData == null)
670 originalData = new Object[data.length][3];
671 for (int i = 0; i < data.length; i++)
673 System.arraycopy(data[i], 0, originalData[i], 0, 3);
678 updateOriginalData(data);
681 table.setModel(new FeatureTableModel(data));
682 table.getColumnModel().getColumn(0).setPreferredWidth(200);
684 groupPanel.setLayout(
685 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
686 pruneGroups(foundGroups);
687 groupPanel.validate();
689 updateFeatureRenderer(data, groupChanged != null);
690 resettingTable = false;
694 * Updates 'originalData' (used for restore on Cancel) if we detect that
695 * changes have been made outwith this dialog
697 * <li>a new feature type added (and made visible)</li>
698 * <li>a feature colour changed (in the Amend Features dialog)</li>
703 protected void updateOriginalData(Object[][] foundData)
705 // todo LinkedHashMap instead of Object[][] would be nice
707 Object[][] currentData = ((FeatureTableModel) table.getModel())
709 for (Object[] row : foundData)
711 String type = (String) row[0];
712 boolean found = false;
713 for (Object[] current : currentData)
715 if (type.equals(current[0]))
719 * currently dependent on object equality here;
720 * really need an equals method on FeatureColour
722 if (!row[1].equals(current[1]))
725 * feature colour has changed externally - update originalData
727 for (Object[] original : originalData)
729 if (type.equals(original[0]))
731 original[1] = row[1];
742 * new feature detected - add to original data (on top)
744 Object[][] newData = new Object[originalData.length + 1][3];
745 for (int i = 0; i < originalData.length; i++)
747 System.arraycopy(originalData[i], 0, newData[i + 1], 0, 3);
750 originalData = newData;
756 * Remove from the groups panel any checkboxes for groups that are not in the
757 * foundGroups set. This enables removing a group from the display when the
758 * last feature in that group is deleted.
762 protected void pruneGroups(Set<String> foundGroups)
764 for (int g = 0; g < groupPanel.getComponentCount(); g++)
766 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
767 if (!foundGroups.contains(checkbox.getText()))
769 groupPanel.remove(checkbox);
775 * reorder data based on the featureRenderers global priority list.
779 private void ensureOrder(Object[][] data)
781 boolean sort = false;
782 float[] order = new float[data.length];
783 for (int i = 0; i < order.length; i++)
785 order[i] = fr.getOrder(data[i][0].toString());
788 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
792 sort = sort || order[i - 1] > order[i];
797 jalview.util.QuickSort.sort(order, data);
803 JalviewFileChooser chooser = new JalviewFileChooser("fc",
804 "Sequence Feature Colours");
805 chooser.setFileView(new JalviewFileView());
806 chooser.setDialogTitle(
807 MessageManager.getString("label.load_feature_colours"));
808 chooser.setToolTipText(MessageManager.getString("action.load"));
810 int value = chooser.showOpenDialog(this);
812 if (value == JalviewFileChooser.APPROVE_OPTION)
814 File file = chooser.getSelectedFile();
818 InputStreamReader in = new InputStreamReader(
819 new FileInputStream(file), "UTF-8");
821 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
823 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
826 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
829 Color mincol = null, maxcol = null;
832 mincol = new Color(Integer.parseInt(newcol.getMinRGB(), 16));
833 maxcol = new Color(Integer.parseInt(newcol.getRGB(), 16));
835 } catch (Exception e)
837 Cache.log.warn("Couldn't parse out graduated feature color.",
840 FeatureColourI gcol = new FeatureColour(mincol, maxcol,
841 newcol.getMin(), newcol.getMax());
842 if (newcol.hasAutoScale())
844 gcol.setAutoScaled(newcol.getAutoScale());
846 if (newcol.hasColourByLabel())
848 gcol.setColourByLabel(newcol.getColourByLabel());
850 if (newcol.hasThreshold())
852 gcol.setThreshold(newcol.getThreshold());
854 if (newcol.getThreshType().length() > 0)
856 String ttyp = newcol.getThreshType();
857 if (ttyp.equalsIgnoreCase("ABOVE"))
859 gcol.setAboveThreshold(true);
861 if (ttyp.equalsIgnoreCase("BELOW"))
863 gcol.setBelowThreshold(true);
866 fr.setColour(name = newcol.getName(), gcol);
870 Color color = new Color(
871 Integer.parseInt(jucs.getColour(i).getRGB(), 16));
872 fr.setColour(name = jucs.getColour(i).getName(),
873 new FeatureColour(color));
875 fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
880 Object[][] data = ((FeatureTableModel) table.getModel())
883 updateFeatureRenderer(data, false);
886 } catch (Exception ex)
888 System.out.println("Error loading User Colour File\n" + ex);
895 JalviewFileChooser chooser = new JalviewFileChooser("fc",
896 "Sequence Feature Colours");
897 chooser.setFileView(new JalviewFileView());
898 chooser.setDialogTitle(
899 MessageManager.getString("label.save_feature_colours"));
900 chooser.setToolTipText(MessageManager.getString("action.save"));
902 int value = chooser.showSaveDialog(this);
904 if (value == JalviewFileChooser.APPROVE_OPTION)
906 String choice = chooser.getSelectedFile().getPath();
907 jalview.schemabinding.version2.JalviewUserColours ucs = new jalview.schemabinding.version2.JalviewUserColours();
908 ucs.setSchemeName("Sequence Features");
911 PrintWriter out = new PrintWriter(new OutputStreamWriter(
912 new FileOutputStream(choice), "UTF-8"));
914 Set<String> fr_colours = fr.getAllFeatureColours();
915 Iterator<String> e = fr_colours.iterator();
916 float[] sortOrder = new float[fr_colours.size()];
917 String[] sortTypes = new String[fr_colours.size()];
921 sortTypes[i] = e.next();
922 sortOrder[i] = fr.getOrder(sortTypes[i]);
925 QuickSort.sort(sortOrder, sortTypes);
927 for (i = 0; i < sortTypes.length; i++)
929 jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
930 col.setName(sortTypes[i]);
931 FeatureColourI fcol = fr.getFeatureStyle(sortTypes[i]);
932 if (fcol.isSimpleColour())
934 col.setRGB(Format.getHexString(fcol.getColour()));
938 col.setRGB(Format.getHexString(fcol.getMaxColour()));
939 col.setMin(fcol.getMin());
940 col.setMax(fcol.getMax());
942 jalview.util.Format.getHexString(fcol.getMinColour()));
943 col.setAutoScale(fcol.isAutoScaled());
944 col.setThreshold(fcol.getThreshold());
945 col.setColourByLabel(fcol.isColourByLabel());
946 col.setThreshType(fcol.isAboveThreshold() ? "ABOVE"
947 : (fcol.isBelowThreshold() ? "BELOW" : "NONE"));
953 } catch (Exception ex)
955 ex.printStackTrace();
960 public void invertSelection()
962 for (int i = 0; i < table.getRowCount(); i++)
964 Boolean value = (Boolean) table.getValueAt(i, 2);
966 table.setValueAt(new Boolean(!value.booleanValue()), i, 2);
970 public void orderByAvWidth()
972 if (table == null || table.getModel() == null)
976 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
977 float[] width = new float[data.length];
981 for (int i = 0; i < data.length; i++)
983 awidth = typeWidth.get(data[i][0]);
986 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
987 // weight - but have to make per
988 // sequence, too (awidth[2])
989 // if (width[i]==1) // hack to distinguish single width sequences.
1001 boolean sort = false;
1002 for (int i = 0; i < width.length; i++)
1004 // awidth = (float[]) typeWidth.get(data[i][0]);
1007 width[i] = fr.getOrder(data[i][0].toString());
1010 width[i] = fr.setOrder(data[i][0].toString(), i / data.length);
1015 width[i] /= max; // normalize
1016 fr.setOrder(data[i][0].toString(), width[i]); // store for later
1020 sort = sort || width[i - 1] > width[i];
1025 jalview.util.QuickSort.sort(width, data);
1026 // update global priority order
1029 updateFeatureRenderer(data, false);
1037 frame.setClosed(true);
1038 } catch (Exception exe)
1044 public void updateFeatureRenderer(Object[][] data)
1046 updateFeatureRenderer(data, true);
1050 * Update the priority order of features; only repaint if this changed the
1051 * order of visible features
1056 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1058 if (fr.setFeaturePriority(data, visibleNew))
1060 af.alignPanel.paintAlignment(true);
1064 int selectedRow = -1;
1066 JTabbedPane tabbedPane = new JTabbedPane();
1068 BorderLayout borderLayout1 = new BorderLayout();
1070 BorderLayout borderLayout2 = new BorderLayout();
1072 BorderLayout borderLayout3 = new BorderLayout();
1074 JPanel bigPanel = new JPanel();
1076 BorderLayout borderLayout4 = new BorderLayout();
1078 JButton invert = new JButton();
1080 JPanel buttonPanel = new JPanel();
1082 JButton cancel = new JButton();
1084 JButton ok = new JButton();
1086 JButton loadColours = new JButton();
1088 JButton saveColours = new JButton();
1090 JPanel dasButtonPanel = new JPanel();
1092 JButton fetchDAS = new JButton();
1094 JButton saveDAS = new JButton();
1096 JButton cancelDAS = new JButton();
1098 JButton optimizeOrder = new JButton();
1100 JButton sortByScore = new JButton();
1102 JButton sortByDens = new JButton();
1104 JButton help = new JButton();
1106 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1108 private void jbInit() throws Exception
1110 this.setLayout(borderLayout1);
1111 settingsPane.setLayout(borderLayout2);
1112 dasSettingsPane.setLayout(borderLayout3);
1113 bigPanel.setLayout(borderLayout4);
1115 groupPanel = new JPanel();
1116 bigPanel.add(groupPanel, BorderLayout.NORTH);
1118 invert.setFont(JvSwingUtils.getLabelFont());
1119 invert.setText(MessageManager.getString("label.invert_selection"));
1120 invert.addActionListener(new ActionListener()
1123 public void actionPerformed(ActionEvent e)
1128 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1129 optimizeOrder.setText(MessageManager.getString("label.optimise_order"));
1130 optimizeOrder.addActionListener(new ActionListener()
1133 public void actionPerformed(ActionEvent e)
1138 sortByScore.setFont(JvSwingUtils.getLabelFont());
1140 .setText(MessageManager.getString("label.seq_sort_by_score"));
1141 sortByScore.addActionListener(new ActionListener()
1144 public void actionPerformed(ActionEvent e)
1146 af.avc.sortAlignmentByFeatureScore(null);
1149 sortByDens.setFont(JvSwingUtils.getLabelFont());
1151 MessageManager.getString("label.sequence_sort_by_density"));
1152 sortByDens.addActionListener(new ActionListener()
1155 public void actionPerformed(ActionEvent e)
1157 af.avc.sortAlignmentByFeatureDensity(null);
1160 help.setFont(JvSwingUtils.getLabelFont());
1161 help.setText(MessageManager.getString("action.help"));
1162 help.addActionListener(new ActionListener()
1165 public void actionPerformed(ActionEvent e)
1169 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1170 } catch (HelpSetException e1)
1172 e1.printStackTrace();
1176 help.setFont(JvSwingUtils.getLabelFont());
1177 help.setText(MessageManager.getString("action.help"));
1178 help.addActionListener(new ActionListener()
1181 public void actionPerformed(ActionEvent e)
1185 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1186 } catch (HelpSetException e1)
1188 e1.printStackTrace();
1192 cancel.setFont(JvSwingUtils.getLabelFont());
1193 cancel.setText(MessageManager.getString("action.cancel"));
1194 cancel.addActionListener(new ActionListener()
1197 public void actionPerformed(ActionEvent e)
1199 fr.setTransparency(originalTransparency);
1200 updateFeatureRenderer(originalData);
1204 ok.setFont(JvSwingUtils.getLabelFont());
1205 ok.setText(MessageManager.getString("action.ok"));
1206 ok.addActionListener(new ActionListener()
1209 public void actionPerformed(ActionEvent e)
1214 loadColours.setFont(JvSwingUtils.getLabelFont());
1215 loadColours.setText(MessageManager.getString("label.load_colours"));
1216 loadColours.addActionListener(new ActionListener()
1219 public void actionPerformed(ActionEvent e)
1224 saveColours.setFont(JvSwingUtils.getLabelFont());
1225 saveColours.setText(MessageManager.getString("label.save_colours"));
1226 saveColours.addActionListener(new ActionListener()
1229 public void actionPerformed(ActionEvent e)
1234 transparency.addChangeListener(new ChangeListener()
1237 public void stateChanged(ChangeEvent evt)
1239 fr.setTransparency((100 - transparency.getValue()) / 100f);
1240 af.alignPanel.paintAlignment(true);
1244 transparency.setMaximum(70);
1245 transparency.setToolTipText(
1246 MessageManager.getString("label.transparency_tip"));
1247 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1248 fetchDAS.addActionListener(new ActionListener()
1251 public void actionPerformed(ActionEvent e)
1253 fetchDAS_actionPerformed(e);
1256 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1257 saveDAS.addActionListener(new ActionListener()
1260 public void actionPerformed(ActionEvent e)
1262 saveDAS_actionPerformed(e);
1265 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1266 dasSettingsPane.setBorder(null);
1267 cancelDAS.setEnabled(false);
1268 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1269 cancelDAS.addActionListener(new ActionListener()
1272 public void actionPerformed(ActionEvent e)
1274 cancelDAS_actionPerformed(e);
1277 this.add(tabbedPane, java.awt.BorderLayout.CENTER);
1278 tabbedPane.addTab(MessageManager.getString("label.feature_settings"),
1280 tabbedPane.addTab(MessageManager.getString("label.das_settings"),
1282 bigPanel.add(transPanel, java.awt.BorderLayout.SOUTH);
1283 transbuttons.add(optimizeOrder);
1284 transbuttons.add(invert);
1285 transbuttons.add(sortByScore);
1286 transbuttons.add(sortByDens);
1287 transbuttons.add(help);
1288 JPanel sliderPanel = new JPanel();
1289 sliderPanel.add(transparency);
1290 transPanel.add(transparency);
1291 transPanel.add(transbuttons);
1292 buttonPanel.add(ok);
1293 buttonPanel.add(cancel);
1294 buttonPanel.add(loadColours);
1295 buttonPanel.add(saveColours);
1296 bigPanel.add(scrollPane, java.awt.BorderLayout.CENTER);
1297 dasSettingsPane.add(dasButtonPanel, java.awt.BorderLayout.SOUTH);
1298 dasButtonPanel.add(fetchDAS);
1299 dasButtonPanel.add(cancelDAS);
1300 dasButtonPanel.add(saveDAS);
1301 settingsPane.add(bigPanel, java.awt.BorderLayout.CENTER);
1302 settingsPane.add(buttonPanel, java.awt.BorderLayout.SOUTH);
1305 public void fetchDAS_actionPerformed(ActionEvent e)
1307 fetchDAS.setEnabled(false);
1308 cancelDAS.setEnabled(true);
1309 dassourceBrowser.setGuiEnabled(false);
1310 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1311 .getSelectedSources();
1312 doDasFeatureFetch(selectedSources, true, true);
1316 * get the features from selectedSources for all or the current selection
1318 * @param selectedSources
1319 * @param checkDbRefs
1320 * @param promptFetchDbRefs
1322 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1323 boolean checkDbRefs, boolean promptFetchDbRefs)
1325 SequenceI[] dataset, seqs;
1327 AlignmentViewport vp = af.getViewport();
1328 if (vp.getSelectionGroup() != null
1329 && vp.getSelectionGroup().getSize() > 0)
1331 iSize = vp.getSelectionGroup().getSize();
1332 dataset = new SequenceI[iSize];
1333 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1337 iSize = vp.getAlignment().getHeight();
1338 seqs = vp.getAlignment().getSequencesArray();
1341 dataset = new SequenceI[iSize];
1342 for (int i = 0; i < iSize; i++)
1344 dataset[i] = seqs[i].getDatasetSequence();
1347 cancelDAS.setEnabled(true);
1348 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1349 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1350 af.getViewport().setShowSequenceFeatures(true);
1351 af.showSeqFeatures.setSelected(true);
1355 * blocking call to initialise the das source browser
1357 public void initDasSources()
1359 dassourceBrowser.initDasSources();
1363 * examine the current list of das sources and return any matching the given
1364 * nicknames in sources
1367 * Vector of Strings to resolve to DAS source nicknames.
1368 * @return sources that are present in source list.
1370 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1372 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1376 * get currently selected das sources. ensure you have called initDasSources
1377 * before calling this.
1379 * @return vector of selected das source nicknames
1381 public Vector<jalviewSourceI> getSelectedSources()
1383 return dassourceBrowser.getSelectedSources();
1387 * properly initialise DAS fetcher and then initiate a new thread to fetch
1388 * features from the named sources (rather than any turned on by default)
1392 * if true then runs in same thread, otherwise passes to the Swing
1395 public void fetchDasFeatures(Vector<String> sources, boolean block)
1398 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1399 .resolveSourceNicknames(sources);
1400 if (resolved.size() == 0)
1402 resolved = dassourceBrowser.getSelectedSources();
1404 if (resolved.size() > 0)
1406 final List<jalviewSourceI> dassources = resolved;
1407 fetchDAS.setEnabled(false);
1408 // cancelDAS.setEnabled(true); doDasFetch does this.
1409 Runnable fetcher = new Runnable()
1415 doDasFeatureFetch(dassources, true, false);
1425 SwingUtilities.invokeLater(fetcher);
1430 public void saveDAS_actionPerformed(ActionEvent e)
1433 .saveProperties(jalview.bin.Cache.applicationProperties);
1436 public void complete()
1438 fetchDAS.setEnabled(true);
1439 cancelDAS.setEnabled(false);
1440 dassourceBrowser.setGuiEnabled(true);
1444 public void cancelDAS_actionPerformed(ActionEvent e)
1446 if (dasFeatureFetcher != null)
1448 dasFeatureFetcher.cancel();
1453 public void noDasSourceActive()
1456 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1457 MessageManager.getString("label.no_das_sources_selected_warn"),
1458 MessageManager.getString("label.no_das_sources_selected_title"),
1459 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1462 // ///////////////////////////////////////////////////////////////////////
1463 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1464 // ///////////////////////////////////////////////////////////////////////
1465 class FeatureTableModel extends AbstractTableModel
1467 FeatureTableModel(Object[][] data)
1472 private String[] columnNames = {
1473 MessageManager.getString("label.feature_type"),
1474 MessageManager.getString("action.colour"),
1475 MessageManager.getString("label.display") };
1477 private Object[][] data;
1479 public Object[][] getData()
1484 public void setData(Object[][] data)
1490 public int getColumnCount()
1492 return columnNames.length;
1495 public Object[] getRow(int row)
1501 public int getRowCount()
1507 public String getColumnName(int col)
1509 return columnNames[col];
1513 public Object getValueAt(int row, int col)
1515 return data[row][col];
1519 public Class getColumnClass(int c)
1521 return getValueAt(0, c).getClass();
1525 public boolean isCellEditable(int row, int col)
1527 return col == 0 ? false : true;
1531 public void setValueAt(Object value, int row, int col)
1533 data[row][col] = value;
1534 fireTableCellUpdated(row, col);
1535 updateFeatureRenderer(data);
1540 class ColorRenderer extends JLabel implements TableCellRenderer
1542 javax.swing.border.Border unselectedBorder = null;
1544 javax.swing.border.Border selectedBorder = null;
1546 final String baseTT = "Click to edit, right/apple click for menu.";
1548 public ColorRenderer()
1550 setOpaque(true); // MUST do this for background to show up.
1551 setHorizontalTextPosition(SwingConstants.CENTER);
1552 setVerticalTextPosition(SwingConstants.CENTER);
1556 public Component getTableCellRendererComponent(JTable tbl, Object color,
1557 boolean isSelected, boolean hasFocus, int row, int column)
1559 FeatureColourI cellColour = (FeatureColourI) color;
1560 // JLabel comp = new JLabel();
1564 // setBounds(getBounds());
1566 setToolTipText(baseTT);
1567 setBackground(tbl.getBackground());
1568 if (!cellColour.isSimpleColour())
1570 Rectangle cr = tbl.getCellRect(row, column, false);
1571 FeatureSettings.renderGraduatedColor(this, cellColour,
1572 (int) cr.getWidth(), (int) cr.getHeight());
1579 newColor = cellColour.getColour();
1580 setBackground(newColor);
1584 if (selectedBorder == null)
1586 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1587 tbl.getSelectionBackground());
1589 setBorder(selectedBorder);
1593 if (unselectedBorder == null)
1595 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1596 tbl.getBackground());
1598 setBorder(unselectedBorder);
1606 * update comp using rendering settings from gcol
1611 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1613 int w = comp.getWidth(), h = comp.getHeight();
1616 w = (int) comp.getPreferredSize().getWidth();
1617 h = (int) comp.getPreferredSize().getHeight();
1624 renderGraduatedColor(comp, gcol, w, h);
1627 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1630 boolean thr = false;
1633 if (gcol.isAboveThreshold())
1637 tt += "Thresholded (Above " + gcol.getThreshold() + ") ";
1639 if (gcol.isBelowThreshold())
1643 tt += "Thresholded (Below " + gcol.getThreshold() + ") ";
1645 if (gcol.isColourByLabel())
1647 tt = "Coloured by label text. " + tt;
1657 Color newColor = gcol.getMaxColour();
1658 comp.setBackground(newColor);
1659 // System.err.println("Width is " + w / 2);
1660 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1661 comp.setIcon(ficon);
1662 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1663 // + newColor.getGreen() + ", " + newColor.getBlue()
1664 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1665 // + ", " + minCol.getBlue() + ")");
1667 comp.setHorizontalAlignment(SwingConstants.CENTER);
1669 if (tt.length() > 0)
1671 if (comp.getToolTipText() == null)
1673 comp.setToolTipText(tt);
1677 comp.setToolTipText(tt + " " + comp.getToolTipText());
1683 class FeatureIcon implements Icon
1685 FeatureColourI gcol;
1689 boolean midspace = false;
1691 int width = 50, height = 20;
1693 int s1, e1; // start and end of midpoint band for thresholded symbol
1695 Color mpcolour = Color.white;
1697 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1717 public int getIconWidth()
1723 public int getIconHeight()
1729 public void paintIcon(Component c, Graphics g, int x, int y)
1732 if (gcol.isColourByLabel())
1735 g.fillRect(0, 0, width, height);
1736 // need an icon here.
1737 g.setColor(gcol.getMaxColour());
1739 g.setFont(new Font("Verdana", Font.PLAIN, 9));
1741 // g.setFont(g.getFont().deriveFont(
1742 // AffineTransform.getScaleInstance(
1743 // width/g.getFontMetrics().stringWidth("Label"),
1744 // height/g.getFontMetrics().getHeight())));
1746 g.drawString(MessageManager.getString("label.label"), 0, 0);
1751 Color minCol = gcol.getMinColour();
1753 g.fillRect(0, 0, s1, height);
1756 g.setColor(Color.white);
1757 g.fillRect(s1, 0, e1 - s1, height);
1759 g.setColor(gcol.getMaxColour());
1760 g.fillRect(0, e1, width - e1, height);
1765 class ColorEditor extends AbstractCellEditor
1766 implements TableCellEditor, ActionListener
1770 FeatureColourI currentColor;
1772 FeatureColourChooser chooser;
1778 JColorChooser colorChooser;
1782 protected static final String EDIT = "edit";
1784 int selectedRow = 0;
1786 public ColorEditor(FeatureSettings me)
1789 // Set up the editor (from the table's point of view),
1790 // which is a button.
1791 // This button brings up the color chooser dialog,
1792 // which is the editor from the user's point of view.
1793 button = new JButton();
1794 button.setActionCommand(EDIT);
1795 button.addActionListener(this);
1796 button.setBorderPainted(false);
1797 // Set up the dialog that the button brings up.
1798 colorChooser = new JColorChooser();
1799 dialog = JColorChooser.createDialog(button, "Select new Colour", true, // modal
1800 colorChooser, this, // OK button handler
1801 null); // no CANCEL button handler
1805 * Handles events from the editor button and from the dialog's OK button.
1808 public void actionPerformed(ActionEvent e)
1811 if (EDIT.equals(e.getActionCommand()))
1813 // The user has clicked the cell, so
1814 // bring up the dialog.
1815 if (currentColor.isSimpleColour())
1817 // bring up simple color chooser
1818 button.setBackground(currentColor.getColour());
1819 colorChooser.setColor(currentColor.getColour());
1820 dialog.setVisible(true);
1824 // bring up graduated chooser.
1825 chooser = new FeatureColourChooser(me.fr, type);
1826 chooser.setRequestFocusEnabled(true);
1827 chooser.requestFocus();
1828 chooser.addActionListener(this);
1830 // Make the renderer reappear.
1831 fireEditingStopped();
1835 { // User pressed dialog's "OK" button.
1836 if (currentColor.isSimpleColour())
1838 currentColor = new FeatureColour(colorChooser.getColor());
1842 currentColor = chooser.getLastColour();
1844 me.table.setValueAt(getCellEditorValue(), selectedRow, 1);
1845 fireEditingStopped();
1846 me.table.validate();
1850 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1852 public Object getCellEditorValue()
1854 return currentColor;
1857 // Implement the one method defined by TableCellEditor.
1859 public Component getTableCellEditorComponent(JTable table, Object value,
1860 boolean isSelected, int row, int column)
1862 currentColor = (FeatureColourI) value;
1863 this.selectedRow = row;
1864 type = me.table.getValueAt(row, 0).toString();
1865 button.setOpaque(true);
1866 button.setBackground(me.getBackground());
1867 if (!currentColor.isSimpleColour())
1869 JLabel btn = new JLabel();
1870 btn.setSize(button.getSize());
1871 FeatureSettings.renderGraduatedColor(btn, currentColor);
1872 button.setBackground(btn.getBackground());
1873 button.setIcon(btn.getIcon());
1874 button.setText(btn.getText());
1879 button.setIcon(null);
1880 button.setBackground(currentColor.getColour());