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 implements
101 FeatureSettingsControllerI
103 DasSourceBrowser dassourceBrowser;
105 jalview.ws.DasSequenceFeatureFetcher dasFeatureFetcher;
107 JPanel settingsPane = new JPanel();
109 JPanel dasSettingsPane = new JPanel();
111 final FeatureRenderer fr;
113 public final AlignFrame af;
115 Object[][] originalData;
117 private float originalTransparency;
119 final JInternalFrame frame;
121 JScrollPane scrollPane = new JScrollPane();
127 JSlider transparency = new JSlider();
129 JPanel transPanel = new JPanel(new GridLayout(1, 2));
131 private static final int MIN_WIDTH = 400;
133 private static final int MIN_HEIGHT = 400;
140 public FeatureSettings(AlignFrame af)
143 fr = af.getFeatureRenderer();
144 // allow transparency to be recovered
145 transparency.setMaximum(100 - (int) ((originalTransparency = fr
146 .getTransparency()) * 100));
151 } catch (Exception ex)
153 ex.printStackTrace();
159 public String getToolTipText(MouseEvent e)
161 if (table.columnAtPoint(e.getPoint()) == 0)
164 * Tooltip for feature name only
166 return JvSwingUtils.wrapTooltip(true, MessageManager
167 .getString("label.feature_settings_click_drag"));
172 table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
173 table.setFont(new Font("Verdana", Font.PLAIN, 12));
174 table.setDefaultRenderer(Color.class, new ColorRenderer());
176 table.setDefaultEditor(Color.class, new ColorEditor(this));
178 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
179 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
180 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
182 table.addMouseListener(new MouseAdapter()
185 public void mousePressed(MouseEvent evt)
187 selectedRow = table.rowAtPoint(evt.getPoint());
188 if (evt.isPopupTrigger())
190 popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
191 table.getValueAt(selectedRow, 1), fr.getMinMax(),
192 evt.getX(), evt.getY());
194 else if (evt.getClickCount() == 2)
196 boolean invertSelection = evt.isAltDown();
197 boolean toggleSelection = Platform.isControlDown(evt);
198 boolean extendSelection = evt.isShiftDown();
199 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
200 invertSelection, extendSelection, toggleSelection,
201 (String) table.getValueAt(selectedRow, 0));
205 // isPopupTrigger fires on mouseReleased on Windows
207 public void mouseReleased(MouseEvent evt)
209 selectedRow = table.rowAtPoint(evt.getPoint());
210 if (evt.isPopupTrigger())
212 popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
213 table.getValueAt(selectedRow, 1), fr.getMinMax(),
214 evt.getX(), evt.getY());
219 table.addMouseMotionListener(new MouseMotionAdapter()
222 public void mouseDragged(MouseEvent evt)
224 int newRow = table.rowAtPoint(evt.getPoint());
225 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
228 * reposition 'selectedRow' to 'newRow' (the dragged to location)
229 * this could be more than one row away for a very fast drag action
230 * so just swap it with adjacent rows until we get it there
232 Object[][] data = ((FeatureTableModel) table.getModel())
234 int direction = newRow < selectedRow ? -1 : 1;
235 for (int i = selectedRow; i != newRow; i += direction)
237 Object[] temp = data[i];
238 data[i] = data[i + direction];
239 data[i + direction] = temp;
241 updateFeatureRenderer(data);
243 selectedRow = newRow;
247 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
248 // MessageManager.getString("label.feature_settings_click_drag")));
249 scrollPane.setViewportView(table);
251 dassourceBrowser = new DasSourceBrowser(this);
252 dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
254 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
256 fr.findAllFeatures(true); // display everything!
259 discoverAllFeatureData();
260 final PropertyChangeListener change;
261 final FeatureSettings fs = this;
262 fr.addPropertyChangeListener(change = new PropertyChangeListener()
265 public void propertyChange(PropertyChangeEvent evt)
267 if (!fs.resettingTable && !fs.handlingUpdate)
269 fs.handlingUpdate = true;
270 fs.resetTable(null); // new groups may be added with new seuqence
271 // feature types only
272 fs.handlingUpdate = false;
278 frame = new JInternalFrame();
279 frame.setContentPane(this);
280 if (Platform.isAMac())
282 Desktop.addInternalFrame(frame,
283 MessageManager.getString("label.sequence_feature_settings"),
288 Desktop.addInternalFrame(frame,
289 MessageManager.getString("label.sequence_feature_settings"),
292 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
294 frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
297 public void internalFrameClosed(
298 javax.swing.event.InternalFrameEvent evt)
300 fr.removePropertyChangeListener(change);
301 dassourceBrowser.fs = null;
304 frame.setLayer(JLayeredPane.PALETTE_LAYER);
307 protected void popupSort(final int selectedRow, final String type,
308 final Object typeCol, final Map<String, float[][]> minmax, int x,
311 final FeatureColourI featureColour = (FeatureColourI) typeCol;
313 JPopupMenu men = new JPopupMenu(MessageManager.formatMessage(
314 "label.settings_for_param", new String[] { type }));
315 JMenuItem scr = new JMenuItem(
316 MessageManager.getString("label.sort_by_score"));
318 final FeatureSettings me = this;
319 scr.addActionListener(new ActionListener()
323 public void actionPerformed(ActionEvent e)
325 me.af.avc.sortAlignmentByFeatureScore(Arrays
326 .asList(new String[] { type }));
330 JMenuItem dens = new JMenuItem(
331 MessageManager.getString("label.sort_by_density"));
332 dens.addActionListener(new ActionListener()
336 public void actionPerformed(ActionEvent e)
338 me.af.avc.sortAlignmentByFeatureDensity(Arrays
339 .asList(new String[] { type }));
346 final float[][] typeMinMax = minmax.get(type);
348 * final JCheckBoxMenuItem chb = new JCheckBoxMenuItem("Vary Height"); //
349 * this is broken at the moment and isn't that useful anyway!
350 * chb.setSelected(minmax.get(type) != null); chb.addActionListener(new
353 * public void actionPerformed(ActionEvent e) {
354 * chb.setState(chb.getState()); if (chb.getState()) { minmax.put(type,
355 * null); } else { minmax.put(type, typeMinMax); } }
361 if (typeMinMax != null && typeMinMax[0] != null)
363 // if (table.getValueAt(row, column));
364 // graduated colourschemes for those where minmax exists for the
365 // positional features
366 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
368 mxcol.setSelected(!featureColour.isSimpleColour());
370 mxcol.addActionListener(new ActionListener()
372 JColorChooser colorChooser;
375 public void actionPerformed(ActionEvent e)
377 if (e.getSource() == mxcol)
379 if (featureColour.isSimpleColour())
381 FeatureColourChooser fc = new FeatureColourChooser(me.fr,
383 fc.addActionListener(this);
387 // bring up simple color chooser
388 colorChooser = new JColorChooser();
389 JDialog dialog = JColorChooser.createDialog(me,
390 "Select new Colour", true, // modal
391 colorChooser, this, // OK button handler
392 null); // no CANCEL button handler
393 colorChooser.setColor(featureColour.getMaxColour());
394 dialog.setVisible(true);
399 if (e.getSource() instanceof FeatureColourChooser)
401 FeatureColourChooser fc = (FeatureColourChooser) e
403 table.setValueAt(fc.getLastColour(), selectedRow, 1);
408 // probably the color chooser!
410 new FeatureColour(colorChooser.getColor()),
413 me.updateFeatureRenderer(
414 ((FeatureTableModel) table.getModel()).getData(),
423 JMenuItem selCols = new JMenuItem(
424 MessageManager.getString("label.select_columns_containing"));
425 selCols.addActionListener(new ActionListener()
428 public void actionPerformed(ActionEvent arg0)
430 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
434 JMenuItem clearCols = new JMenuItem(
435 MessageManager.getString("label.select_columns_not_containing"));
436 clearCols.addActionListener(new ActionListener()
439 public void actionPerformed(ActionEvent arg0)
441 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
445 JMenuItem hideCols = new JMenuItem(
446 MessageManager.getString("label.hide_columns_containing"));
447 hideCols.addActionListener(new ActionListener()
450 public void actionPerformed(ActionEvent arg0)
452 fr.ap.alignFrame.hideFeatureColumns(type, true);
455 JMenuItem hideOtherCols = new JMenuItem(
456 MessageManager.getString("label.hide_columns_not_containing"));
457 hideOtherCols.addActionListener(new ActionListener()
460 public void actionPerformed(ActionEvent arg0)
462 fr.ap.alignFrame.hideFeatureColumns(type, false);
468 men.add(hideOtherCols);
469 men.show(table, x, y);
473 * true when Feature Settings are updating from feature renderer
475 private boolean handlingUpdate = false;
478 * contains a float[3] for each feature type string. created by setTableData
480 Map<String, float[]> typeWidth = null;
483 synchronized public void discoverAllFeatureData()
485 Vector<String> allFeatures = new Vector<String>();
486 Vector<String> allGroups = new Vector<String>();
487 SequenceFeature[] tmpfeatures;
489 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
491 tmpfeatures = af.getViewport().getAlignment().getSequenceAt(i)
492 .getSequenceFeatures();
493 if (tmpfeatures == null)
499 while (index < tmpfeatures.length)
501 if (tmpfeatures[index].begin == 0 && tmpfeatures[index].end == 0)
507 if (tmpfeatures[index].getFeatureGroup() != null)
509 group = tmpfeatures[index].featureGroup;
510 if (!allGroups.contains(group))
512 allGroups.addElement(group);
513 checkGroupState(group);
517 if (!allFeatures.contains(tmpfeatures[index].getType()))
519 allFeatures.addElement(tmpfeatures[index].getType());
531 * Synchronise gui group list and check visibility of group
534 * @return true if group is visible
536 private boolean checkGroupState(String group)
538 boolean visible = fr.checkGroupVisibility(group, true);
540 for (int g = 0; g < groupPanel.getComponentCount(); g++)
542 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
544 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
549 final String grp = group;
550 final JCheckBox check = new JCheckBox(group, visible);
551 check.setFont(new Font("Serif", Font.BOLD, 12));
552 check.addItemListener(new ItemListener()
555 public void itemStateChanged(ItemEvent evt)
557 fr.setGroupVisibility(check.getText(), check.isSelected());
558 af.alignPanel.getSeqPanel().seqCanvas.repaint();
559 if (af.alignPanel.overviewPanel != null)
561 af.alignPanel.overviewPanel.updateOverviewImage();
564 resetTable(new String[] { grp });
567 groupPanel.add(check);
571 boolean resettingTable = false;
573 synchronized void resetTable(String[] groupChanged)
575 if (resettingTable == true)
579 resettingTable = true;
580 typeWidth = new Hashtable<String, float[]>();
581 // TODO: change avWidth calculation to 'per-sequence' average and use long
583 float[] avWidth = null;
584 SequenceFeature[] tmpfeatures;
585 String group = null, type;
586 Vector<String> visibleChecks = new Vector<String>();
587 Set<String> foundGroups = new HashSet<String>();
589 // Find out which features should be visible depending on which groups
590 // are selected / deselected
591 // and recompute average width ordering
592 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
595 tmpfeatures = af.getViewport().getAlignment().getSequenceAt(i)
596 .getSequenceFeatures();
597 if (tmpfeatures == null)
603 while (index < tmpfeatures.length)
605 group = tmpfeatures[index].featureGroup;
606 foundGroups.add(group);
608 if (tmpfeatures[index].begin == 0 && tmpfeatures[index].end == 0)
614 if (group == null || checkGroupState(group))
616 type = tmpfeatures[index].getType();
617 if (!visibleChecks.contains(type))
619 visibleChecks.addElement(type);
622 if (!typeWidth.containsKey(tmpfeatures[index].getType()))
624 typeWidth.put(tmpfeatures[index].getType(),
625 avWidth = new float[3]);
629 avWidth = typeWidth.get(tmpfeatures[index].getType());
632 if (tmpfeatures[index].getBegin() > tmpfeatures[index].getEnd())
634 avWidth[1] += 1 + tmpfeatures[index].getBegin()
635 - tmpfeatures[index].getEnd();
639 avWidth[1] += 1 + tmpfeatures[index].getEnd()
640 - tmpfeatures[index].getBegin();
646 int fSize = visibleChecks.size();
647 Object[][] data = new Object[fSize][3];
650 if (fr.hasRenderOrder())
654 fr.findAllFeatures(groupChanged != null); // prod to update
655 // colourschemes. but don't
657 // First add the checks in the previous render order,
658 // in case the window has been closed and reopened
660 List<String> frl = fr.getRenderOrder();
661 for (int ro = frl.size() - 1; ro > -1; ro--)
665 if (!visibleChecks.contains(type))
670 data[dataIndex][0] = type;
671 data[dataIndex][1] = fr.getFeatureStyle(type);
672 data[dataIndex][2] = new Boolean(af.getViewport()
673 .getFeaturesDisplayed().isVisible(type));
675 visibleChecks.removeElement(type);
679 fSize = visibleChecks.size();
680 for (int i = 0; i < fSize; i++)
682 // These must be extra features belonging to the group
683 // which was just selected
684 type = visibleChecks.elementAt(i).toString();
685 data[dataIndex][0] = type;
687 data[dataIndex][1] = fr.getFeatureStyle(type);
688 if (data[dataIndex][1] == null)
690 // "Colour has been updated in another view!!"
691 fr.clearRenderOrder();
695 data[dataIndex][2] = new Boolean(true);
699 if (originalData == null)
701 originalData = new Object[data.length][3];
702 for (int i = 0; i < data.length; i++)
704 System.arraycopy(data[i], 0, originalData[i], 0, 3);
709 updateOriginalData(data);
712 table.setModel(new FeatureTableModel(data));
713 table.getColumnModel().getColumn(0).setPreferredWidth(200);
715 groupPanel.setLayout(new GridLayout(fr.getFeatureGroupsSize() / 4 + 1,
717 pruneGroups(foundGroups);
718 groupPanel.validate();
720 updateFeatureRenderer(data, groupChanged != null);
721 resettingTable = false;
725 * Updates 'originalData' (used for restore on Cancel) if we detect that
726 * changes have been made outwith this dialog
728 * <li>a new feature type added (and made visible)</li>
729 * <li>a feature colour changed (in the Amend Features dialog)</li>
734 protected void updateOriginalData(Object[][] foundData)
736 // todo LinkedHashMap instead of Object[][] would be nice
738 Object[][] currentData = ((FeatureTableModel) table.getModel())
740 for (Object[] row : foundData)
742 String type = (String) row[0];
743 boolean found = false;
744 for (Object[] current : currentData)
746 if (type.equals(current[0]))
750 * currently dependent on object equality here;
751 * really need an equals method on FeatureColour
753 if (!row[1].equals(current[1]))
756 * feature colour has changed externally - update originalData
758 for (Object[] original : originalData)
760 if (type.equals(original[0]))
762 original[1] = row[1];
773 * new feature detected - add to original data (on top)
775 Object[][] newData = new Object[originalData.length + 1][3];
776 for (int i = 0; i < originalData.length; i++)
778 System.arraycopy(originalData[i], 0, newData[i + 1], 0, 3);
781 originalData = newData;
787 * Remove from the groups panel any checkboxes for groups that are not in the
788 * foundGroups set. This enables removing a group from the display when the
789 * last feature in that group is deleted.
793 protected void pruneGroups(Set<String> foundGroups)
795 for (int g = 0; g < groupPanel.getComponentCount(); g++)
797 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
798 if (!foundGroups.contains(checkbox.getText()))
800 groupPanel.remove(checkbox);
806 * reorder data based on the featureRenderers global priority list.
810 private void ensureOrder(Object[][] data)
812 boolean sort = false;
813 float[] order = new float[data.length];
814 for (int i = 0; i < order.length; i++)
816 order[i] = fr.getOrder(data[i][0].toString());
819 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
823 sort = sort || order[i - 1] > order[i];
828 jalview.util.QuickSort.sort(order, data);
834 JalviewFileChooser chooser = new JalviewFileChooser("fc",
835 "Sequence Feature Colours");
836 chooser.setFileView(new JalviewFileView());
837 chooser.setDialogTitle(MessageManager
838 .getString("label.load_feature_colours"));
839 chooser.setToolTipText(MessageManager.getString("action.load"));
841 int value = chooser.showOpenDialog(this);
843 if (value == JalviewFileChooser.APPROVE_OPTION)
845 File file = chooser.getSelectedFile();
849 InputStreamReader in = new InputStreamReader(new FileInputStream(
852 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
854 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
857 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
860 Color mincol = null, maxcol = null;
863 mincol = new Color(Integer.parseInt(newcol.getMinRGB(), 16));
864 maxcol = new Color(Integer.parseInt(newcol.getRGB(), 16));
866 } catch (Exception e)
868 Cache.log.warn("Couldn't parse out graduated feature color.",
871 FeatureColourI gcol = new FeatureColour(mincol, maxcol,
872 newcol.getMin(), newcol.getMax());
873 if (newcol.hasAutoScale())
875 gcol.setAutoScaled(newcol.getAutoScale());
877 if (newcol.hasColourByLabel())
879 gcol.setColourByLabel(newcol.getColourByLabel());
881 if (newcol.hasThreshold())
883 gcol.setThreshold(newcol.getThreshold());
885 if (newcol.getThreshType().length() > 0)
887 String ttyp = newcol.getThreshType();
888 if (ttyp.equalsIgnoreCase("ABOVE"))
890 gcol.setAboveThreshold(true);
892 if (ttyp.equalsIgnoreCase("BELOW"))
894 gcol.setBelowThreshold(true);
897 fr.setColour(name = newcol.getName(), gcol);
901 Color color = new Color(
902 Integer.parseInt(jucs.getColour(i).getRGB(), 16));
903 fr.setColour(name = jucs.getColour(i).getName(),
904 new FeatureColour(color));
906 fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
911 Object[][] data = ((FeatureTableModel) table.getModel())
914 updateFeatureRenderer(data, false);
917 } catch (Exception ex)
919 System.out.println("Error loading User Colour File\n" + ex);
926 JalviewFileChooser chooser = new JalviewFileChooser("fc",
927 "Sequence Feature Colours");
928 chooser.setFileView(new JalviewFileView());
929 chooser.setDialogTitle(MessageManager
930 .getString("label.save_feature_colours"));
931 chooser.setToolTipText(MessageManager.getString("action.save"));
933 int value = chooser.showSaveDialog(this);
935 if (value == JalviewFileChooser.APPROVE_OPTION)
937 String choice = chooser.getSelectedFile().getPath();
938 jalview.schemabinding.version2.JalviewUserColours ucs = new jalview.schemabinding.version2.JalviewUserColours();
939 ucs.setSchemeName("Sequence Features");
942 PrintWriter out = new PrintWriter(new OutputStreamWriter(
943 new FileOutputStream(choice), "UTF-8"));
945 Set<String> fr_colours = fr.getAllFeatureColours();
946 Iterator<String> e = fr_colours.iterator();
947 float[] sortOrder = new float[fr_colours.size()];
948 String[] sortTypes = new String[fr_colours.size()];
952 sortTypes[i] = e.next();
953 sortOrder[i] = fr.getOrder(sortTypes[i]);
956 QuickSort.sort(sortOrder, sortTypes);
958 for (i = 0; i < sortTypes.length; i++)
960 jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
961 col.setName(sortTypes[i]);
962 FeatureColourI fcol = fr.getFeatureStyle(sortTypes[i]);
963 if (fcol.isSimpleColour())
965 col.setRGB(Format.getHexString(fcol.getColour()));
969 col.setRGB(Format.getHexString(fcol.getMaxColour()));
970 col.setMin(fcol.getMin());
971 col.setMax(fcol.getMax());
972 col.setMinRGB(jalview.util.Format.getHexString(fcol
974 col.setAutoScale(fcol.isAutoScaled());
975 col.setThreshold(fcol.getThreshold());
976 col.setColourByLabel(fcol.isColourByLabel());
977 col.setThreshType(fcol.isAboveThreshold() ? "ABOVE" : (fcol
978 .isBelowThreshold() ? "BELOW" : "NONE"));
984 } catch (Exception ex)
986 ex.printStackTrace();
991 public void invertSelection()
993 for (int i = 0; i < table.getRowCount(); i++)
995 Boolean value = (Boolean) table.getValueAt(i, 2);
997 table.setValueAt(new Boolean(!value.booleanValue()), i, 2);
1001 public void orderByAvWidth()
1003 if (table == null || table.getModel() == null)
1007 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1008 float[] width = new float[data.length];
1012 for (int i = 0; i < data.length; i++)
1014 awidth = typeWidth.get(data[i][0]);
1017 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1018 // weight - but have to make per
1019 // sequence, too (awidth[2])
1020 // if (width[i]==1) // hack to distinguish single width sequences.
1032 boolean sort = false;
1033 for (int i = 0; i < width.length; i++)
1035 // awidth = (float[]) typeWidth.get(data[i][0]);
1038 width[i] = fr.getOrder(data[i][0].toString());
1041 width[i] = fr.setOrder(data[i][0].toString(), i / data.length);
1046 width[i] /= max; // normalize
1047 fr.setOrder(data[i][0].toString(), width[i]); // store for later
1051 sort = sort || width[i - 1] > width[i];
1056 jalview.util.QuickSort.sort(width, data);
1057 // update global priority order
1060 updateFeatureRenderer(data, false);
1068 frame.setClosed(true);
1069 } catch (Exception exe)
1075 public void updateFeatureRenderer(Object[][] data)
1077 updateFeatureRenderer(data, true);
1081 * Update the priority order of features; only repaint if this changed the
1082 * order of visible features
1087 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1089 if (fr.setFeaturePriority(data, visibleNew))
1091 af.alignPanel.paintAlignment(true);
1095 int selectedRow = -1;
1097 JTabbedPane tabbedPane = new JTabbedPane();
1099 BorderLayout borderLayout1 = new BorderLayout();
1101 BorderLayout borderLayout2 = new BorderLayout();
1103 BorderLayout borderLayout3 = new BorderLayout();
1105 JPanel bigPanel = new JPanel();
1107 BorderLayout borderLayout4 = new BorderLayout();
1109 JButton invert = new JButton();
1111 JPanel buttonPanel = new JPanel();
1113 JButton cancel = new JButton();
1115 JButton ok = new JButton();
1117 JButton loadColours = new JButton();
1119 JButton saveColours = new JButton();
1121 JPanel dasButtonPanel = new JPanel();
1123 JButton fetchDAS = new JButton();
1125 JButton saveDAS = new JButton();
1127 JButton cancelDAS = new JButton();
1129 JButton optimizeOrder = new JButton();
1131 JButton sortByScore = new JButton();
1133 JButton sortByDens = new JButton();
1135 JButton help = new JButton();
1137 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1139 private void jbInit() throws Exception
1141 this.setLayout(borderLayout1);
1142 settingsPane.setLayout(borderLayout2);
1143 dasSettingsPane.setLayout(borderLayout3);
1144 bigPanel.setLayout(borderLayout4);
1146 groupPanel = new JPanel();
1147 bigPanel.add(groupPanel, BorderLayout.NORTH);
1149 invert.setFont(JvSwingUtils.getLabelFont());
1150 invert.setText(MessageManager.getString("label.invert_selection"));
1151 invert.addActionListener(new ActionListener()
1154 public void actionPerformed(ActionEvent e)
1159 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1160 optimizeOrder.setText(MessageManager.getString("label.optimise_order"));
1161 optimizeOrder.addActionListener(new ActionListener()
1164 public void actionPerformed(ActionEvent e)
1169 sortByScore.setFont(JvSwingUtils.getLabelFont());
1171 .setText(MessageManager.getString("label.seq_sort_by_score"));
1172 sortByScore.addActionListener(new ActionListener()
1175 public void actionPerformed(ActionEvent e)
1177 af.avc.sortAlignmentByFeatureScore(null);
1180 sortByDens.setFont(JvSwingUtils.getLabelFont());
1181 sortByDens.setText(MessageManager
1182 .getString("label.sequence_sort_by_density"));
1183 sortByDens.addActionListener(new ActionListener()
1186 public void actionPerformed(ActionEvent e)
1188 af.avc.sortAlignmentByFeatureDensity(null);
1191 help.setFont(JvSwingUtils.getLabelFont());
1192 help.setText(MessageManager.getString("action.help"));
1193 help.addActionListener(new ActionListener()
1196 public void actionPerformed(ActionEvent e)
1200 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1201 } catch (HelpSetException e1)
1203 e1.printStackTrace();
1207 help.setFont(JvSwingUtils.getLabelFont());
1208 help.setText(MessageManager.getString("action.help"));
1209 help.addActionListener(new ActionListener()
1212 public void actionPerformed(ActionEvent e)
1216 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1217 } catch (HelpSetException e1)
1219 e1.printStackTrace();
1223 cancel.setFont(JvSwingUtils.getLabelFont());
1224 cancel.setText(MessageManager.getString("action.cancel"));
1225 cancel.addActionListener(new ActionListener()
1228 public void actionPerformed(ActionEvent e)
1230 fr.setTransparency(originalTransparency);
1231 updateFeatureRenderer(originalData);
1235 ok.setFont(JvSwingUtils.getLabelFont());
1236 ok.setText(MessageManager.getString("action.ok"));
1237 ok.addActionListener(new ActionListener()
1240 public void actionPerformed(ActionEvent e)
1245 loadColours.setFont(JvSwingUtils.getLabelFont());
1246 loadColours.setText(MessageManager.getString("label.load_colours"));
1247 loadColours.addActionListener(new ActionListener()
1250 public void actionPerformed(ActionEvent e)
1255 saveColours.setFont(JvSwingUtils.getLabelFont());
1256 saveColours.setText(MessageManager.getString("label.save_colours"));
1257 saveColours.addActionListener(new ActionListener()
1260 public void actionPerformed(ActionEvent e)
1265 transparency.addChangeListener(new ChangeListener()
1268 public void stateChanged(ChangeEvent evt)
1270 fr.setTransparency((100 - transparency.getValue()) / 100f);
1271 af.alignPanel.paintAlignment(true);
1275 transparency.setMaximum(70);
1276 transparency.setToolTipText(MessageManager
1277 .getString("label.transparency_tip"));
1278 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1279 fetchDAS.addActionListener(new ActionListener()
1282 public void actionPerformed(ActionEvent e)
1284 fetchDAS_actionPerformed(e);
1287 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1288 saveDAS.addActionListener(new ActionListener()
1291 public void actionPerformed(ActionEvent e)
1293 saveDAS_actionPerformed(e);
1296 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1297 dasSettingsPane.setBorder(null);
1298 cancelDAS.setEnabled(false);
1299 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1300 cancelDAS.addActionListener(new ActionListener()
1303 public void actionPerformed(ActionEvent e)
1305 cancelDAS_actionPerformed(e);
1308 this.add(tabbedPane, java.awt.BorderLayout.CENTER);
1309 tabbedPane.addTab(MessageManager.getString("label.feature_settings"),
1311 tabbedPane.addTab(MessageManager.getString("label.das_settings"),
1313 bigPanel.add(transPanel, java.awt.BorderLayout.SOUTH);
1314 transbuttons.add(optimizeOrder);
1315 transbuttons.add(invert);
1316 transbuttons.add(sortByScore);
1317 transbuttons.add(sortByDens);
1318 transbuttons.add(help);
1319 JPanel sliderPanel = new JPanel();
1320 sliderPanel.add(transparency);
1321 transPanel.add(transparency);
1322 transPanel.add(transbuttons);
1323 buttonPanel.add(ok);
1324 buttonPanel.add(cancel);
1325 buttonPanel.add(loadColours);
1326 buttonPanel.add(saveColours);
1327 bigPanel.add(scrollPane, java.awt.BorderLayout.CENTER);
1328 dasSettingsPane.add(dasButtonPanel, java.awt.BorderLayout.SOUTH);
1329 dasButtonPanel.add(fetchDAS);
1330 dasButtonPanel.add(cancelDAS);
1331 dasButtonPanel.add(saveDAS);
1332 settingsPane.add(bigPanel, java.awt.BorderLayout.CENTER);
1333 settingsPane.add(buttonPanel, java.awt.BorderLayout.SOUTH);
1336 public void fetchDAS_actionPerformed(ActionEvent e)
1338 fetchDAS.setEnabled(false);
1339 cancelDAS.setEnabled(true);
1340 dassourceBrowser.setGuiEnabled(false);
1341 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1342 .getSelectedSources();
1343 doDasFeatureFetch(selectedSources, true, true);
1347 * get the features from selectedSources for all or the current selection
1349 * @param selectedSources
1350 * @param checkDbRefs
1351 * @param promptFetchDbRefs
1353 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1354 boolean checkDbRefs, boolean promptFetchDbRefs)
1356 SequenceI[] dataset, seqs;
1358 AlignmentViewport vp = af.getViewport();
1359 if (vp.getSelectionGroup() != null
1360 && vp.getSelectionGroup().getSize() > 0)
1362 iSize = vp.getSelectionGroup().getSize();
1363 dataset = new SequenceI[iSize];
1364 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1368 iSize = vp.getAlignment().getHeight();
1369 seqs = vp.getAlignment().getSequencesArray();
1372 dataset = new SequenceI[iSize];
1373 for (int i = 0; i < iSize; i++)
1375 dataset[i] = seqs[i].getDatasetSequence();
1378 cancelDAS.setEnabled(true);
1379 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1380 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1381 af.getViewport().setShowSequenceFeatures(true);
1382 af.showSeqFeatures.setSelected(true);
1386 * blocking call to initialise the das source browser
1388 public void initDasSources()
1390 dassourceBrowser.initDasSources();
1394 * examine the current list of das sources and return any matching the given
1395 * nicknames in sources
1398 * Vector of Strings to resolve to DAS source nicknames.
1399 * @return sources that are present in source list.
1401 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1403 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1407 * get currently selected das sources. ensure you have called initDasSources
1408 * before calling this.
1410 * @return vector of selected das source nicknames
1412 public Vector<jalviewSourceI> getSelectedSources()
1414 return dassourceBrowser.getSelectedSources();
1418 * properly initialise DAS fetcher and then initiate a new thread to fetch
1419 * features from the named sources (rather than any turned on by default)
1423 * if true then runs in same thread, otherwise passes to the Swing
1426 public void fetchDasFeatures(Vector<String> sources, boolean block)
1429 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1430 .resolveSourceNicknames(sources);
1431 if (resolved.size() == 0)
1433 resolved = dassourceBrowser.getSelectedSources();
1435 if (resolved.size() > 0)
1437 final List<jalviewSourceI> dassources = resolved;
1438 fetchDAS.setEnabled(false);
1439 // cancelDAS.setEnabled(true); doDasFetch does this.
1440 Runnable fetcher = new Runnable()
1446 doDasFeatureFetch(dassources, true, false);
1456 SwingUtilities.invokeLater(fetcher);
1461 public void saveDAS_actionPerformed(ActionEvent e)
1464 .saveProperties(jalview.bin.Cache.applicationProperties);
1467 public void complete()
1469 fetchDAS.setEnabled(true);
1470 cancelDAS.setEnabled(false);
1471 dassourceBrowser.setGuiEnabled(true);
1475 public void cancelDAS_actionPerformed(ActionEvent e)
1477 if (dasFeatureFetcher != null)
1479 dasFeatureFetcher.cancel();
1484 public void noDasSourceActive()
1488 .showInternalConfirmDialog(
1491 .getString("label.no_das_sources_selected_warn"),
1493 .getString("label.no_das_sources_selected_title"),
1494 JvOptionPane.DEFAULT_OPTION,
1495 JvOptionPane.INFORMATION_MESSAGE);
1498 // ///////////////////////////////////////////////////////////////////////
1499 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1500 // ///////////////////////////////////////////////////////////////////////
1501 class FeatureTableModel extends AbstractTableModel
1503 FeatureTableModel(Object[][] data)
1508 private String[] columnNames = {
1509 MessageManager.getString("label.feature_type"),
1510 MessageManager.getString("action.colour"),
1511 MessageManager.getString("label.display") };
1513 private Object[][] data;
1515 public Object[][] getData()
1520 public void setData(Object[][] data)
1526 public int getColumnCount()
1528 return columnNames.length;
1531 public Object[] getRow(int row)
1537 public int getRowCount()
1543 public String getColumnName(int col)
1545 return columnNames[col];
1549 public Object getValueAt(int row, int col)
1551 return data[row][col];
1555 public Class getColumnClass(int c)
1557 return getValueAt(0, c).getClass();
1561 public boolean isCellEditable(int row, int col)
1563 return col == 0 ? false : true;
1567 public void setValueAt(Object value, int row, int col)
1569 data[row][col] = value;
1570 fireTableCellUpdated(row, col);
1571 updateFeatureRenderer(data);
1576 class ColorRenderer extends JLabel implements TableCellRenderer
1578 javax.swing.border.Border unselectedBorder = null;
1580 javax.swing.border.Border selectedBorder = null;
1582 final String baseTT = "Click to edit, right/apple click for menu.";
1584 public ColorRenderer()
1586 setOpaque(true); // MUST do this for background to show up.
1587 setHorizontalTextPosition(SwingConstants.CENTER);
1588 setVerticalTextPosition(SwingConstants.CENTER);
1592 public Component getTableCellRendererComponent(JTable tbl,
1593 Object color, boolean isSelected, boolean hasFocus, int row,
1596 FeatureColourI cellColour = (FeatureColourI) color;
1597 // JLabel comp = new JLabel();
1601 // setBounds(getBounds());
1603 setToolTipText(baseTT);
1604 setBackground(tbl.getBackground());
1605 if (!cellColour.isSimpleColour())
1607 Rectangle cr = tbl.getCellRect(row, column, false);
1608 FeatureSettings.renderGraduatedColor(this, cellColour,
1609 (int) cr.getWidth(), (int) cr.getHeight());
1616 newColor = cellColour.getColour();
1617 setBackground(newColor);
1621 if (selectedBorder == null)
1623 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1624 tbl.getSelectionBackground());
1626 setBorder(selectedBorder);
1630 if (unselectedBorder == null)
1632 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1633 tbl.getBackground());
1635 setBorder(unselectedBorder);
1643 * update comp using rendering settings from gcol
1648 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1650 int w = comp.getWidth(), h = comp.getHeight();
1653 w = (int) comp.getPreferredSize().getWidth();
1654 h = (int) comp.getPreferredSize().getHeight();
1661 renderGraduatedColor(comp, gcol, w, h);
1664 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1667 boolean thr = false;
1670 if (gcol.isAboveThreshold())
1674 tt += "Thresholded (Above " + gcol.getThreshold() + ") ";
1676 if (gcol.isBelowThreshold())
1680 tt += "Thresholded (Below " + gcol.getThreshold() + ") ";
1682 if (gcol.isColourByLabel())
1684 tt = "Coloured by label text. " + tt;
1694 Color newColor = gcol.getMaxColour();
1695 comp.setBackground(newColor);
1696 // System.err.println("Width is " + w / 2);
1697 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1698 comp.setIcon(ficon);
1699 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1700 // + newColor.getGreen() + ", " + newColor.getBlue()
1701 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1702 // + ", " + minCol.getBlue() + ")");
1704 comp.setHorizontalAlignment(SwingConstants.CENTER);
1706 if (tt.length() > 0)
1708 if (comp.getToolTipText() == null)
1710 comp.setToolTipText(tt);
1714 comp.setToolTipText(tt + " " + comp.getToolTipText());
1720 class FeatureIcon implements Icon
1722 FeatureColourI gcol;
1726 boolean midspace = false;
1728 int width = 50, height = 20;
1730 int s1, e1; // start and end of midpoint band for thresholded symbol
1732 Color mpcolour = Color.white;
1734 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1754 public int getIconWidth()
1760 public int getIconHeight()
1766 public void paintIcon(Component c, Graphics g, int x, int y)
1769 if (gcol.isColourByLabel())
1772 g.fillRect(0, 0, width, height);
1773 // need an icon here.
1774 g.setColor(gcol.getMaxColour());
1776 g.setFont(new Font("Verdana", Font.PLAIN, 9));
1778 // g.setFont(g.getFont().deriveFont(
1779 // AffineTransform.getScaleInstance(
1780 // width/g.getFontMetrics().stringWidth("Label"),
1781 // height/g.getFontMetrics().getHeight())));
1783 g.drawString(MessageManager.getString("label.label"), 0, 0);
1788 Color minCol = gcol.getMinColour();
1790 g.fillRect(0, 0, s1, height);
1793 g.setColor(Color.white);
1794 g.fillRect(s1, 0, e1 - s1, height);
1796 g.setColor(gcol.getMaxColour());
1797 g.fillRect(0, e1, width - e1, height);
1802 class ColorEditor extends AbstractCellEditor implements TableCellEditor,
1807 FeatureColourI currentColor;
1809 FeatureColourChooser chooser;
1815 JColorChooser colorChooser;
1819 protected static final String EDIT = "edit";
1821 int selectedRow = 0;
1823 public ColorEditor(FeatureSettings me)
1826 // Set up the editor (from the table's point of view),
1827 // which is a button.
1828 // This button brings up the color chooser dialog,
1829 // which is the editor from the user's point of view.
1830 button = new JButton();
1831 button.setActionCommand(EDIT);
1832 button.addActionListener(this);
1833 button.setBorderPainted(false);
1834 // Set up the dialog that the button brings up.
1835 colorChooser = new JColorChooser();
1836 dialog = JColorChooser.createDialog(button, "Select new Colour", true, // modal
1837 colorChooser, this, // OK button handler
1838 null); // no CANCEL button handler
1842 * Handles events from the editor button and from the dialog's OK button.
1845 public void actionPerformed(ActionEvent e)
1848 if (EDIT.equals(e.getActionCommand()))
1850 // The user has clicked the cell, so
1851 // bring up the dialog.
1852 if (currentColor.isSimpleColour())
1854 // bring up simple color chooser
1855 button.setBackground(currentColor.getColour());
1856 colorChooser.setColor(currentColor.getColour());
1857 dialog.setVisible(true);
1861 // bring up graduated chooser.
1862 chooser = new FeatureColourChooser(me.fr, type);
1863 chooser.setRequestFocusEnabled(true);
1864 chooser.requestFocus();
1865 chooser.addActionListener(this);
1867 // Make the renderer reappear.
1868 fireEditingStopped();
1872 { // User pressed dialog's "OK" button.
1873 if (currentColor.isSimpleColour())
1875 currentColor = new FeatureColour(colorChooser.getColor());
1879 currentColor = chooser.getLastColour();
1881 me.table.setValueAt(getCellEditorValue(), selectedRow, 1);
1882 fireEditingStopped();
1883 me.table.validate();
1887 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1889 public Object getCellEditorValue()
1891 return currentColor;
1894 // Implement the one method defined by TableCellEditor.
1896 public Component getTableCellEditorComponent(JTable table, Object value,
1897 boolean isSelected, int row, int column)
1899 currentColor = (FeatureColourI) value;
1900 this.selectedRow = row;
1901 type = me.table.getValueAt(row, 0).toString();
1902 button.setOpaque(true);
1903 button.setBackground(me.getBackground());
1904 if (!currentColor.isSimpleColour())
1906 JLabel btn = new JLabel();
1907 btn.setSize(button.getSize());
1908 FeatureSettings.renderGraduatedColor(btn, currentColor);
1909 button.setBackground(btn.getBackground());
1910 button.setIcon(btn.getIcon());
1911 button.setText(btn.getText());
1916 button.setIcon(null);
1917 button.setBackground(currentColor.getColour());