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.SequenceFeature;
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 * contains a float[3] for each feature type string. created by setTableData
483 Map<String, float[]> typeWidth = null;
486 synchronized public void discoverAllFeatureData()
488 Vector<String> allFeatures = new Vector<String>();
489 Vector<String> allGroups = new Vector<String>();
490 SequenceFeature[] tmpfeatures;
492 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
494 tmpfeatures = af.getViewport().getAlignment().getSequenceAt(i)
495 .getSequenceFeatures();
496 if (tmpfeatures == null)
502 while (index < tmpfeatures.length)
504 if (tmpfeatures[index].begin == 0 && tmpfeatures[index].end == 0)
510 if (tmpfeatures[index].getFeatureGroup() != null)
512 group = tmpfeatures[index].featureGroup;
513 if (!allGroups.contains(group))
515 allGroups.addElement(group);
516 checkGroupState(group);
520 if (!allFeatures.contains(tmpfeatures[index].getType()))
522 allFeatures.addElement(tmpfeatures[index].getType());
534 * Synchronise gui group list and check visibility of group
537 * @return true if group is visible
539 private boolean checkGroupState(String group)
541 boolean visible = fr.checkGroupVisibility(group, true);
543 for (int g = 0; g < groupPanel.getComponentCount(); g++)
545 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
547 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
552 final String grp = group;
553 final JCheckBox check = new JCheckBox(group, visible);
554 check.setFont(new Font("Serif", Font.BOLD, 12));
555 check.addItemListener(new ItemListener()
558 public void itemStateChanged(ItemEvent evt)
560 fr.setGroupVisibility(check.getText(), check.isSelected());
561 af.alignPanel.getSeqPanel().seqCanvas.repaint();
562 if (af.alignPanel.overviewPanel != null)
564 af.alignPanel.overviewPanel.updateOverviewImage();
567 resetTable(new String[] { grp });
570 groupPanel.add(check);
574 boolean resettingTable = false;
576 synchronized void resetTable(String[] groupChanged)
578 if (resettingTable == true)
582 resettingTable = true;
583 typeWidth = new Hashtable<String, float[]>();
584 // TODO: change avWidth calculation to 'per-sequence' average and use long
586 float[] avWidth = null;
587 SequenceFeature[] tmpfeatures;
588 String group = null, type;
589 Vector<String> visibleChecks = new Vector<String>();
590 Set<String> foundGroups = new HashSet<String>();
592 // Find out which features should be visible depending on which groups
593 // are selected / deselected
594 // and recompute average width ordering
595 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
598 tmpfeatures = af.getViewport().getAlignment().getSequenceAt(i)
599 .getSequenceFeatures();
600 if (tmpfeatures == null)
606 while (index < tmpfeatures.length)
608 group = tmpfeatures[index].featureGroup;
609 foundGroups.add(group);
611 if (tmpfeatures[index].begin == 0 && tmpfeatures[index].end == 0)
617 if (group == null || checkGroupState(group))
619 type = tmpfeatures[index].getType();
620 if (!visibleChecks.contains(type))
622 visibleChecks.addElement(type);
625 if (!typeWidth.containsKey(tmpfeatures[index].getType()))
627 typeWidth.put(tmpfeatures[index].getType(),
628 avWidth = new float[3]);
632 avWidth = typeWidth.get(tmpfeatures[index].getType());
635 if (tmpfeatures[index].getBegin() > tmpfeatures[index].getEnd())
637 avWidth[1] += 1 + tmpfeatures[index].getBegin()
638 - tmpfeatures[index].getEnd();
642 avWidth[1] += 1 + tmpfeatures[index].getEnd()
643 - tmpfeatures[index].getBegin();
649 int fSize = visibleChecks.size();
650 Object[][] data = new Object[fSize][3];
653 if (fr.hasRenderOrder())
657 fr.findAllFeatures(groupChanged != null); // prod to update
658 // colourschemes. but don't
660 // First add the checks in the previous render order,
661 // in case the window has been closed and reopened
663 List<String> frl = fr.getRenderOrder();
664 for (int ro = frl.size() - 1; ro > -1; ro--)
668 if (!visibleChecks.contains(type))
673 data[dataIndex][0] = type;
674 data[dataIndex][1] = fr.getFeatureStyle(type);
675 data[dataIndex][2] = new Boolean(
676 af.getViewport().getFeaturesDisplayed().isVisible(type));
678 visibleChecks.removeElement(type);
682 fSize = visibleChecks.size();
683 for (int i = 0; i < fSize; i++)
685 // These must be extra features belonging to the group
686 // which was just selected
687 type = visibleChecks.elementAt(i).toString();
688 data[dataIndex][0] = type;
690 data[dataIndex][1] = fr.getFeatureStyle(type);
691 if (data[dataIndex][1] == null)
693 // "Colour has been updated in another view!!"
694 fr.clearRenderOrder();
698 data[dataIndex][2] = new Boolean(true);
702 if (originalData == null)
704 originalData = new Object[data.length][3];
705 for (int i = 0; i < data.length; i++)
707 System.arraycopy(data[i], 0, originalData[i], 0, 3);
712 updateOriginalData(data);
715 table.setModel(new FeatureTableModel(data));
716 table.getColumnModel().getColumn(0).setPreferredWidth(200);
718 groupPanel.setLayout(
719 new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
720 pruneGroups(foundGroups);
721 groupPanel.validate();
723 updateFeatureRenderer(data, groupChanged != null);
724 resettingTable = false;
728 * Updates 'originalData' (used for restore on Cancel) if we detect that
729 * changes have been made outwith this dialog
731 * <li>a new feature type added (and made visible)</li>
732 * <li>a feature colour changed (in the Amend Features dialog)</li>
737 protected void updateOriginalData(Object[][] foundData)
739 // todo LinkedHashMap instead of Object[][] would be nice
741 Object[][] currentData = ((FeatureTableModel) table.getModel())
743 for (Object[] row : foundData)
745 String type = (String) row[0];
746 boolean found = false;
747 for (Object[] current : currentData)
749 if (type.equals(current[0]))
753 * currently dependent on object equality here;
754 * really need an equals method on FeatureColour
756 if (!row[1].equals(current[1]))
759 * feature colour has changed externally - update originalData
761 for (Object[] original : originalData)
763 if (type.equals(original[0]))
765 original[1] = row[1];
776 * new feature detected - add to original data (on top)
778 Object[][] newData = new Object[originalData.length + 1][3];
779 for (int i = 0; i < originalData.length; i++)
781 System.arraycopy(originalData[i], 0, newData[i + 1], 0, 3);
784 originalData = newData;
790 * Remove from the groups panel any checkboxes for groups that are not in the
791 * foundGroups set. This enables removing a group from the display when the
792 * last feature in that group is deleted.
796 protected void pruneGroups(Set<String> foundGroups)
798 for (int g = 0; g < groupPanel.getComponentCount(); g++)
800 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
801 if (!foundGroups.contains(checkbox.getText()))
803 groupPanel.remove(checkbox);
809 * reorder data based on the featureRenderers global priority list.
813 private void ensureOrder(Object[][] data)
815 boolean sort = false;
816 float[] order = new float[data.length];
817 for (int i = 0; i < order.length; i++)
819 order[i] = fr.getOrder(data[i][0].toString());
822 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
826 sort = sort || order[i - 1] > order[i];
831 jalview.util.QuickSort.sort(order, data);
837 JalviewFileChooser chooser = new JalviewFileChooser("fc",
838 "Sequence Feature Colours");
839 chooser.setFileView(new JalviewFileView());
840 chooser.setDialogTitle(
841 MessageManager.getString("label.load_feature_colours"));
842 chooser.setToolTipText(MessageManager.getString("action.load"));
844 int value = chooser.showOpenDialog(this);
846 if (value == JalviewFileChooser.APPROVE_OPTION)
848 File file = chooser.getSelectedFile();
852 InputStreamReader in = new InputStreamReader(
853 new FileInputStream(file), "UTF-8");
855 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
857 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
860 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
863 Color mincol = null, maxcol = null;
866 mincol = new Color(Integer.parseInt(newcol.getMinRGB(), 16));
867 maxcol = new Color(Integer.parseInt(newcol.getRGB(), 16));
869 } catch (Exception e)
871 Cache.log.warn("Couldn't parse out graduated feature color.",
874 FeatureColourI gcol = new FeatureColour(mincol, maxcol,
875 newcol.getMin(), newcol.getMax());
876 if (newcol.hasAutoScale())
878 gcol.setAutoScaled(newcol.getAutoScale());
880 if (newcol.hasColourByLabel())
882 gcol.setColourByLabel(newcol.getColourByLabel());
884 if (newcol.hasThreshold())
886 gcol.setThreshold(newcol.getThreshold());
888 if (newcol.getThreshType().length() > 0)
890 String ttyp = newcol.getThreshType();
891 if (ttyp.equalsIgnoreCase("ABOVE"))
893 gcol.setAboveThreshold(true);
895 if (ttyp.equalsIgnoreCase("BELOW"))
897 gcol.setBelowThreshold(true);
900 fr.setColour(name = newcol.getName(), gcol);
904 Color color = new Color(
905 Integer.parseInt(jucs.getColour(i).getRGB(), 16));
906 fr.setColour(name = jucs.getColour(i).getName(),
907 new FeatureColour(color));
909 fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
914 Object[][] data = ((FeatureTableModel) table.getModel())
917 updateFeatureRenderer(data, false);
920 } catch (Exception ex)
922 System.out.println("Error loading User Colour File\n" + ex);
929 JalviewFileChooser chooser = new JalviewFileChooser("fc",
930 "Sequence Feature Colours");
931 chooser.setFileView(new JalviewFileView());
932 chooser.setDialogTitle(
933 MessageManager.getString("label.save_feature_colours"));
934 chooser.setToolTipText(MessageManager.getString("action.save"));
936 int value = chooser.showSaveDialog(this);
938 if (value == JalviewFileChooser.APPROVE_OPTION)
940 String choice = chooser.getSelectedFile().getPath();
941 jalview.schemabinding.version2.JalviewUserColours ucs = new jalview.schemabinding.version2.JalviewUserColours();
942 ucs.setSchemeName("Sequence Features");
945 PrintWriter out = new PrintWriter(new OutputStreamWriter(
946 new FileOutputStream(choice), "UTF-8"));
948 Set<String> fr_colours = fr.getAllFeatureColours();
949 Iterator<String> e = fr_colours.iterator();
950 float[] sortOrder = new float[fr_colours.size()];
951 String[] sortTypes = new String[fr_colours.size()];
955 sortTypes[i] = e.next();
956 sortOrder[i] = fr.getOrder(sortTypes[i]);
959 QuickSort.sort(sortOrder, sortTypes);
961 for (i = 0; i < sortTypes.length; i++)
963 jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
964 col.setName(sortTypes[i]);
965 FeatureColourI fcol = fr.getFeatureStyle(sortTypes[i]);
966 if (fcol.isSimpleColour())
968 col.setRGB(Format.getHexString(fcol.getColour()));
972 col.setRGB(Format.getHexString(fcol.getMaxColour()));
973 col.setMin(fcol.getMin());
974 col.setMax(fcol.getMax());
976 jalview.util.Format.getHexString(fcol.getMinColour()));
977 col.setAutoScale(fcol.isAutoScaled());
978 col.setThreshold(fcol.getThreshold());
979 col.setColourByLabel(fcol.isColourByLabel());
980 col.setThreshType(fcol.isAboveThreshold() ? "ABOVE"
981 : (fcol.isBelowThreshold() ? "BELOW" : "NONE"));
987 } catch (Exception ex)
989 ex.printStackTrace();
994 public void invertSelection()
996 for (int i = 0; i < table.getRowCount(); i++)
998 Boolean value = (Boolean) table.getValueAt(i, 2);
1000 table.setValueAt(new Boolean(!value.booleanValue()), i, 2);
1004 public void orderByAvWidth()
1006 if (table == null || table.getModel() == null)
1010 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1011 float[] width = new float[data.length];
1015 for (int i = 0; i < data.length; i++)
1017 awidth = typeWidth.get(data[i][0]);
1020 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1021 // weight - but have to make per
1022 // sequence, too (awidth[2])
1023 // if (width[i]==1) // hack to distinguish single width sequences.
1035 boolean sort = false;
1036 for (int i = 0; i < width.length; i++)
1038 // awidth = (float[]) typeWidth.get(data[i][0]);
1041 width[i] = fr.getOrder(data[i][0].toString());
1044 width[i] = fr.setOrder(data[i][0].toString(), i / data.length);
1049 width[i] /= max; // normalize
1050 fr.setOrder(data[i][0].toString(), width[i]); // store for later
1054 sort = sort || width[i - 1] > width[i];
1059 jalview.util.QuickSort.sort(width, data);
1060 // update global priority order
1063 updateFeatureRenderer(data, false);
1071 frame.setClosed(true);
1072 } catch (Exception exe)
1078 public void updateFeatureRenderer(Object[][] data)
1080 updateFeatureRenderer(data, true);
1084 * Update the priority order of features; only repaint if this changed the
1085 * order of visible features
1090 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1092 if (fr.setFeaturePriority(data, visibleNew))
1094 af.alignPanel.paintAlignment(true);
1098 int selectedRow = -1;
1100 JTabbedPane tabbedPane = new JTabbedPane();
1102 BorderLayout borderLayout1 = new BorderLayout();
1104 BorderLayout borderLayout2 = new BorderLayout();
1106 BorderLayout borderLayout3 = new BorderLayout();
1108 JPanel bigPanel = new JPanel();
1110 BorderLayout borderLayout4 = new BorderLayout();
1112 JButton invert = new JButton();
1114 JPanel buttonPanel = new JPanel();
1116 JButton cancel = new JButton();
1118 JButton ok = new JButton();
1120 JButton loadColours = new JButton();
1122 JButton saveColours = new JButton();
1124 JPanel dasButtonPanel = new JPanel();
1126 JButton fetchDAS = new JButton();
1128 JButton saveDAS = new JButton();
1130 JButton cancelDAS = new JButton();
1132 JButton optimizeOrder = new JButton();
1134 JButton sortByScore = new JButton();
1136 JButton sortByDens = new JButton();
1138 JButton help = new JButton();
1140 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1142 private void jbInit() throws Exception
1144 this.setLayout(borderLayout1);
1145 settingsPane.setLayout(borderLayout2);
1146 dasSettingsPane.setLayout(borderLayout3);
1147 bigPanel.setLayout(borderLayout4);
1149 groupPanel = new JPanel();
1150 bigPanel.add(groupPanel, BorderLayout.NORTH);
1152 invert.setFont(JvSwingUtils.getLabelFont());
1153 invert.setText(MessageManager.getString("label.invert_selection"));
1154 invert.addActionListener(new ActionListener()
1157 public void actionPerformed(ActionEvent e)
1162 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1163 optimizeOrder.setText(MessageManager.getString("label.optimise_order"));
1164 optimizeOrder.addActionListener(new ActionListener()
1167 public void actionPerformed(ActionEvent e)
1172 sortByScore.setFont(JvSwingUtils.getLabelFont());
1174 .setText(MessageManager.getString("label.seq_sort_by_score"));
1175 sortByScore.addActionListener(new ActionListener()
1178 public void actionPerformed(ActionEvent e)
1180 af.avc.sortAlignmentByFeatureScore(null);
1183 sortByDens.setFont(JvSwingUtils.getLabelFont());
1185 MessageManager.getString("label.sequence_sort_by_density"));
1186 sortByDens.addActionListener(new ActionListener()
1189 public void actionPerformed(ActionEvent e)
1191 af.avc.sortAlignmentByFeatureDensity(null);
1194 help.setFont(JvSwingUtils.getLabelFont());
1195 help.setText(MessageManager.getString("action.help"));
1196 help.addActionListener(new ActionListener()
1199 public void actionPerformed(ActionEvent e)
1203 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1204 } catch (HelpSetException e1)
1206 e1.printStackTrace();
1210 help.setFont(JvSwingUtils.getLabelFont());
1211 help.setText(MessageManager.getString("action.help"));
1212 help.addActionListener(new ActionListener()
1215 public void actionPerformed(ActionEvent e)
1219 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1220 } catch (HelpSetException e1)
1222 e1.printStackTrace();
1226 cancel.setFont(JvSwingUtils.getLabelFont());
1227 cancel.setText(MessageManager.getString("action.cancel"));
1228 cancel.addActionListener(new ActionListener()
1231 public void actionPerformed(ActionEvent e)
1233 fr.setTransparency(originalTransparency);
1234 updateFeatureRenderer(originalData);
1238 ok.setFont(JvSwingUtils.getLabelFont());
1239 ok.setText(MessageManager.getString("action.ok"));
1240 ok.addActionListener(new ActionListener()
1243 public void actionPerformed(ActionEvent e)
1248 loadColours.setFont(JvSwingUtils.getLabelFont());
1249 loadColours.setText(MessageManager.getString("label.load_colours"));
1250 loadColours.addActionListener(new ActionListener()
1253 public void actionPerformed(ActionEvent e)
1258 saveColours.setFont(JvSwingUtils.getLabelFont());
1259 saveColours.setText(MessageManager.getString("label.save_colours"));
1260 saveColours.addActionListener(new ActionListener()
1263 public void actionPerformed(ActionEvent e)
1268 transparency.addChangeListener(new ChangeListener()
1271 public void stateChanged(ChangeEvent evt)
1273 fr.setTransparency((100 - transparency.getValue()) / 100f);
1274 af.alignPanel.paintAlignment(true);
1278 transparency.setMaximum(70);
1279 transparency.setToolTipText(
1280 MessageManager.getString("label.transparency_tip"));
1281 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1282 fetchDAS.addActionListener(new ActionListener()
1285 public void actionPerformed(ActionEvent e)
1287 fetchDAS_actionPerformed(e);
1290 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1291 saveDAS.addActionListener(new ActionListener()
1294 public void actionPerformed(ActionEvent e)
1296 saveDAS_actionPerformed(e);
1299 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1300 dasSettingsPane.setBorder(null);
1301 cancelDAS.setEnabled(false);
1302 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1303 cancelDAS.addActionListener(new ActionListener()
1306 public void actionPerformed(ActionEvent e)
1308 cancelDAS_actionPerformed(e);
1311 this.add(tabbedPane, java.awt.BorderLayout.CENTER);
1312 tabbedPane.addTab(MessageManager.getString("label.feature_settings"),
1314 tabbedPane.addTab(MessageManager.getString("label.das_settings"),
1316 bigPanel.add(transPanel, java.awt.BorderLayout.SOUTH);
1317 transbuttons.add(optimizeOrder);
1318 transbuttons.add(invert);
1319 transbuttons.add(sortByScore);
1320 transbuttons.add(sortByDens);
1321 transbuttons.add(help);
1322 JPanel sliderPanel = new JPanel();
1323 sliderPanel.add(transparency);
1324 transPanel.add(transparency);
1325 transPanel.add(transbuttons);
1326 buttonPanel.add(ok);
1327 buttonPanel.add(cancel);
1328 buttonPanel.add(loadColours);
1329 buttonPanel.add(saveColours);
1330 bigPanel.add(scrollPane, java.awt.BorderLayout.CENTER);
1331 dasSettingsPane.add(dasButtonPanel, java.awt.BorderLayout.SOUTH);
1332 dasButtonPanel.add(fetchDAS);
1333 dasButtonPanel.add(cancelDAS);
1334 dasButtonPanel.add(saveDAS);
1335 settingsPane.add(bigPanel, java.awt.BorderLayout.CENTER);
1336 settingsPane.add(buttonPanel, java.awt.BorderLayout.SOUTH);
1339 public void fetchDAS_actionPerformed(ActionEvent e)
1341 fetchDAS.setEnabled(false);
1342 cancelDAS.setEnabled(true);
1343 dassourceBrowser.setGuiEnabled(false);
1344 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1345 .getSelectedSources();
1346 doDasFeatureFetch(selectedSources, true, true);
1350 * get the features from selectedSources for all or the current selection
1352 * @param selectedSources
1353 * @param checkDbRefs
1354 * @param promptFetchDbRefs
1356 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1357 boolean checkDbRefs, boolean promptFetchDbRefs)
1359 SequenceI[] dataset, seqs;
1361 AlignmentViewport vp = af.getViewport();
1362 if (vp.getSelectionGroup() != null
1363 && vp.getSelectionGroup().getSize() > 0)
1365 iSize = vp.getSelectionGroup().getSize();
1366 dataset = new SequenceI[iSize];
1367 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1371 iSize = vp.getAlignment().getHeight();
1372 seqs = vp.getAlignment().getSequencesArray();
1375 dataset = new SequenceI[iSize];
1376 for (int i = 0; i < iSize; i++)
1378 dataset[i] = seqs[i].getDatasetSequence();
1381 cancelDAS.setEnabled(true);
1382 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1383 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1384 af.getViewport().setShowSequenceFeatures(true);
1385 af.showSeqFeatures.setSelected(true);
1389 * blocking call to initialise the das source browser
1391 public void initDasSources()
1393 dassourceBrowser.initDasSources();
1397 * examine the current list of das sources and return any matching the given
1398 * nicknames in sources
1401 * Vector of Strings to resolve to DAS source nicknames.
1402 * @return sources that are present in source list.
1404 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1406 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1410 * get currently selected das sources. ensure you have called initDasSources
1411 * before calling this.
1413 * @return vector of selected das source nicknames
1415 public Vector<jalviewSourceI> getSelectedSources()
1417 return dassourceBrowser.getSelectedSources();
1421 * properly initialise DAS fetcher and then initiate a new thread to fetch
1422 * features from the named sources (rather than any turned on by default)
1426 * if true then runs in same thread, otherwise passes to the Swing
1429 public void fetchDasFeatures(Vector<String> sources, boolean block)
1432 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1433 .resolveSourceNicknames(sources);
1434 if (resolved.size() == 0)
1436 resolved = dassourceBrowser.getSelectedSources();
1438 if (resolved.size() > 0)
1440 final List<jalviewSourceI> dassources = resolved;
1441 fetchDAS.setEnabled(false);
1442 // cancelDAS.setEnabled(true); doDasFetch does this.
1443 Runnable fetcher = new Runnable()
1449 doDasFeatureFetch(dassources, true, false);
1459 SwingUtilities.invokeLater(fetcher);
1464 public void saveDAS_actionPerformed(ActionEvent e)
1467 .saveProperties(jalview.bin.Cache.applicationProperties);
1470 public void complete()
1472 fetchDAS.setEnabled(true);
1473 cancelDAS.setEnabled(false);
1474 dassourceBrowser.setGuiEnabled(true);
1478 public void cancelDAS_actionPerformed(ActionEvent e)
1480 if (dasFeatureFetcher != null)
1482 dasFeatureFetcher.cancel();
1487 public void noDasSourceActive()
1490 JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
1491 MessageManager.getString("label.no_das_sources_selected_warn"),
1492 MessageManager.getString("label.no_das_sources_selected_title"),
1493 JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
1496 // ///////////////////////////////////////////////////////////////////////
1497 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1498 // ///////////////////////////////////////////////////////////////////////
1499 class FeatureTableModel extends AbstractTableModel
1501 FeatureTableModel(Object[][] data)
1506 private String[] columnNames = {
1507 MessageManager.getString("label.feature_type"),
1508 MessageManager.getString("action.colour"),
1509 MessageManager.getString("label.display") };
1511 private Object[][] data;
1513 public Object[][] getData()
1518 public void setData(Object[][] data)
1524 public int getColumnCount()
1526 return columnNames.length;
1529 public Object[] getRow(int row)
1535 public int getRowCount()
1541 public String getColumnName(int col)
1543 return columnNames[col];
1547 public Object getValueAt(int row, int col)
1549 return data[row][col];
1553 public Class getColumnClass(int c)
1555 return getValueAt(0, c).getClass();
1559 public boolean isCellEditable(int row, int col)
1561 return col == 0 ? false : true;
1565 public void setValueAt(Object value, int row, int col)
1567 data[row][col] = value;
1568 fireTableCellUpdated(row, col);
1569 updateFeatureRenderer(data);
1574 class ColorRenderer extends JLabel implements TableCellRenderer
1576 javax.swing.border.Border unselectedBorder = null;
1578 javax.swing.border.Border selectedBorder = null;
1580 final String baseTT = "Click to edit, right/apple click for menu.";
1582 public ColorRenderer()
1584 setOpaque(true); // MUST do this for background to show up.
1585 setHorizontalTextPosition(SwingConstants.CENTER);
1586 setVerticalTextPosition(SwingConstants.CENTER);
1590 public Component getTableCellRendererComponent(JTable tbl, Object color,
1591 boolean isSelected, boolean hasFocus, int row, int column)
1593 FeatureColourI cellColour = (FeatureColourI) color;
1594 // JLabel comp = new JLabel();
1598 // setBounds(getBounds());
1600 setToolTipText(baseTT);
1601 setBackground(tbl.getBackground());
1602 if (!cellColour.isSimpleColour())
1604 Rectangle cr = tbl.getCellRect(row, column, false);
1605 FeatureSettings.renderGraduatedColor(this, cellColour,
1606 (int) cr.getWidth(), (int) cr.getHeight());
1613 newColor = cellColour.getColour();
1614 setBackground(newColor);
1618 if (selectedBorder == null)
1620 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1621 tbl.getSelectionBackground());
1623 setBorder(selectedBorder);
1627 if (unselectedBorder == null)
1629 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1630 tbl.getBackground());
1632 setBorder(unselectedBorder);
1640 * update comp using rendering settings from gcol
1645 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1647 int w = comp.getWidth(), h = comp.getHeight();
1650 w = (int) comp.getPreferredSize().getWidth();
1651 h = (int) comp.getPreferredSize().getHeight();
1658 renderGraduatedColor(comp, gcol, w, h);
1661 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1664 boolean thr = false;
1667 if (gcol.isAboveThreshold())
1671 tt += "Thresholded (Above " + gcol.getThreshold() + ") ";
1673 if (gcol.isBelowThreshold())
1677 tt += "Thresholded (Below " + gcol.getThreshold() + ") ";
1679 if (gcol.isColourByLabel())
1681 tt = "Coloured by label text. " + tt;
1691 Color newColor = gcol.getMaxColour();
1692 comp.setBackground(newColor);
1693 // System.err.println("Width is " + w / 2);
1694 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1695 comp.setIcon(ficon);
1696 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1697 // + newColor.getGreen() + ", " + newColor.getBlue()
1698 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1699 // + ", " + minCol.getBlue() + ")");
1701 comp.setHorizontalAlignment(SwingConstants.CENTER);
1703 if (tt.length() > 0)
1705 if (comp.getToolTipText() == null)
1707 comp.setToolTipText(tt);
1711 comp.setToolTipText(tt + " " + comp.getToolTipText());
1717 class FeatureIcon implements Icon
1719 FeatureColourI gcol;
1723 boolean midspace = false;
1725 int width = 50, height = 20;
1727 int s1, e1; // start and end of midpoint band for thresholded symbol
1729 Color mpcolour = Color.white;
1731 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1751 public int getIconWidth()
1757 public int getIconHeight()
1763 public void paintIcon(Component c, Graphics g, int x, int y)
1766 if (gcol.isColourByLabel())
1769 g.fillRect(0, 0, width, height);
1770 // need an icon here.
1771 g.setColor(gcol.getMaxColour());
1773 g.setFont(new Font("Verdana", Font.PLAIN, 9));
1775 // g.setFont(g.getFont().deriveFont(
1776 // AffineTransform.getScaleInstance(
1777 // width/g.getFontMetrics().stringWidth("Label"),
1778 // height/g.getFontMetrics().getHeight())));
1780 g.drawString(MessageManager.getString("label.label"), 0, 0);
1785 Color minCol = gcol.getMinColour();
1787 g.fillRect(0, 0, s1, height);
1790 g.setColor(Color.white);
1791 g.fillRect(s1, 0, e1 - s1, height);
1793 g.setColor(gcol.getMaxColour());
1794 g.fillRect(0, e1, width - e1, height);
1799 class ColorEditor extends AbstractCellEditor
1800 implements TableCellEditor, ActionListener
1804 FeatureColourI currentColor;
1806 FeatureColourChooser chooser;
1812 JColorChooser colorChooser;
1816 protected static final String EDIT = "edit";
1818 int selectedRow = 0;
1820 public ColorEditor(FeatureSettings me)
1823 // Set up the editor (from the table's point of view),
1824 // which is a button.
1825 // This button brings up the color chooser dialog,
1826 // which is the editor from the user's point of view.
1827 button = new JButton();
1828 button.setActionCommand(EDIT);
1829 button.addActionListener(this);
1830 button.setBorderPainted(false);
1831 // Set up the dialog that the button brings up.
1832 colorChooser = new JColorChooser();
1833 dialog = JColorChooser.createDialog(button, "Select new Colour", true, // modal
1834 colorChooser, this, // OK button handler
1835 null); // no CANCEL button handler
1839 * Handles events from the editor button and from the dialog's OK button.
1842 public void actionPerformed(ActionEvent e)
1845 if (EDIT.equals(e.getActionCommand()))
1847 // The user has clicked the cell, so
1848 // bring up the dialog.
1849 if (currentColor.isSimpleColour())
1851 // bring up simple color chooser
1852 button.setBackground(currentColor.getColour());
1853 colorChooser.setColor(currentColor.getColour());
1854 dialog.setVisible(true);
1858 // bring up graduated chooser.
1859 chooser = new FeatureColourChooser(me.fr, type);
1860 chooser.setRequestFocusEnabled(true);
1861 chooser.requestFocus();
1862 chooser.addActionListener(this);
1864 // Make the renderer reappear.
1865 fireEditingStopped();
1869 { // User pressed dialog's "OK" button.
1870 if (currentColor.isSimpleColour())
1872 currentColor = new FeatureColour(colorChooser.getColor());
1876 currentColor = chooser.getLastColour();
1878 me.table.setValueAt(getCellEditorValue(), selectedRow, 1);
1879 fireEditingStopped();
1880 me.table.validate();
1884 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1886 public Object getCellEditorValue()
1888 return currentColor;
1891 // Implement the one method defined by TableCellEditor.
1893 public Component getTableCellEditorComponent(JTable table, Object value,
1894 boolean isSelected, int row, int column)
1896 currentColor = (FeatureColourI) value;
1897 this.selectedRow = row;
1898 type = me.table.getValueAt(row, 0).toString();
1899 button.setOpaque(true);
1900 button.setBackground(me.getBackground());
1901 if (!currentColor.isSimpleColour())
1903 JLabel btn = new JLabel();
1904 btn.setSize(button.getSize());
1905 FeatureSettings.renderGraduatedColor(btn, currentColor);
1906 button.setBackground(btn.getBackground());
1907 button.setIcon(btn.getIcon());
1908 button.setText(btn.getText());
1913 button.setIcon(null);
1914 button.setBackground(currentColor.getColour());