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.SequenceFeature;
28 import jalview.datamodel.SequenceI;
29 import jalview.gui.Help.HelpId;
30 import jalview.io.JalviewFileChooser;
31 import jalview.io.JalviewFileView;
32 import jalview.schemabinding.version2.JalviewUserColours;
33 import jalview.schemes.FeatureColour;
34 import jalview.util.Format;
35 import jalview.util.MessageManager;
36 import jalview.util.Platform;
37 import jalview.util.QuickSort;
38 import jalview.viewmodel.AlignmentViewport;
39 import jalview.ws.dbsources.das.api.jalviewSourceI;
41 import java.awt.BorderLayout;
42 import java.awt.Color;
43 import java.awt.Component;
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 public FeatureSettings(AlignFrame af)
134 fr = af.getFeatureRenderer();
135 // allow transparency to be recovered
136 transparency.setMaximum(100 - (int) ((originalTransparency = fr
137 .getTransparency()) * 100));
142 } catch (Exception ex)
144 ex.printStackTrace();
150 public String getToolTipText(MouseEvent e)
152 if (table.columnAtPoint(e.getPoint()) == 0)
155 * Tooltip for feature name only
157 return JvSwingUtils.wrapTooltip(true, MessageManager
158 .getString("label.feature_settings_click_drag"));
163 table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
164 table.setFont(new Font("Verdana", Font.PLAIN, 12));
165 table.setDefaultRenderer(Color.class, new ColorRenderer());
167 table.setDefaultEditor(Color.class, new ColorEditor(this));
169 table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
170 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
171 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
173 table.addMouseListener(new MouseAdapter()
176 public void mousePressed(MouseEvent evt)
178 selectedRow = table.rowAtPoint(evt.getPoint());
179 if (evt.isPopupTrigger())
181 popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
182 table.getValueAt(selectedRow, 1), fr.getMinMax(),
183 evt.getX(), evt.getY());
185 else if (evt.getClickCount() == 2)
187 boolean invertSelection = evt.isAltDown();
188 boolean toggleSelection = Platform.isControlDown(evt);
189 boolean extendSelection = evt.isShiftDown();
190 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
191 invertSelection, extendSelection, toggleSelection,
192 (String) table.getValueAt(selectedRow, 0));
196 // isPopupTrigger fires on mouseReleased on Windows
198 public void mouseReleased(MouseEvent evt)
200 selectedRow = table.rowAtPoint(evt.getPoint());
201 if (evt.isPopupTrigger())
203 popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
204 table.getValueAt(selectedRow, 1), fr.getMinMax(),
205 evt.getX(), evt.getY());
210 table.addMouseMotionListener(new MouseMotionAdapter()
213 public void mouseDragged(MouseEvent evt)
215 int newRow = table.rowAtPoint(evt.getPoint());
216 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
219 * reposition 'selectedRow' to 'newRow' (the dragged to location)
220 * this could be more than one row away for a very fast drag action
221 * so just swap it with adjacent rows until we get it there
223 Object[][] data = ((FeatureTableModel) table.getModel())
225 int direction = newRow < selectedRow ? -1 : 1;
226 for (int i = selectedRow; i != newRow; i += direction)
228 Object[] temp = data[i];
229 data[i] = data[i + direction];
230 data[i + direction] = temp;
232 updateFeatureRenderer(data);
234 selectedRow = newRow;
238 // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
239 // MessageManager.getString("label.feature_settings_click_drag")));
240 scrollPane.setViewportView(table);
242 dassourceBrowser = new DasSourceBrowser(this);
243 dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
245 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
247 fr.findAllFeatures(true); // display everything!
250 discoverAllFeatureData();
251 final PropertyChangeListener change;
252 final FeatureSettings fs = this;
253 fr.addPropertyChangeListener(change = new PropertyChangeListener()
256 public void propertyChange(PropertyChangeEvent evt)
258 if (!fs.resettingTable && !fs.handlingUpdate)
260 fs.handlingUpdate = true;
261 fs.resetTable(null); // new groups may be added with new seuqence
262 // feature types only
263 fs.handlingUpdate = false;
269 frame = new JInternalFrame();
270 frame.setContentPane(this);
271 if (Platform.isAMac())
273 Desktop.addInternalFrame(frame,
274 MessageManager.getString("label.sequence_feature_settings"),
279 Desktop.addInternalFrame(frame,
280 MessageManager.getString("label.sequence_feature_settings"),
284 frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
287 public void internalFrameClosed(
288 javax.swing.event.InternalFrameEvent evt)
290 fr.removePropertyChangeListener(change);
291 dassourceBrowser.fs = null;
294 frame.setLayer(JLayeredPane.PALETTE_LAYER);
297 protected void popupSort(final int selectedRow, final String type,
298 final Object typeCol, final Map<String, float[][]> minmax, int x,
301 final FeatureColourI featureColour = (FeatureColourI) typeCol;
303 JPopupMenu men = new JPopupMenu(MessageManager.formatMessage(
304 "label.settings_for_param", new String[] { type }));
305 JMenuItem scr = new JMenuItem(
306 MessageManager.getString("label.sort_by_score"));
308 final FeatureSettings me = this;
309 scr.addActionListener(new ActionListener()
313 public void actionPerformed(ActionEvent e)
315 me.af.avc.sortAlignmentByFeatureScore(Arrays
316 .asList(new String[] { type }));
320 JMenuItem dens = new JMenuItem(
321 MessageManager.getString("label.sort_by_density"));
322 dens.addActionListener(new ActionListener()
326 public void actionPerformed(ActionEvent e)
328 me.af.avc.sortAlignmentByFeatureDensity(Arrays
329 .asList(new String[] { type }));
336 final float[][] typeMinMax = minmax.get(type);
338 * final JCheckBoxMenuItem chb = new JCheckBoxMenuItem("Vary Height"); //
339 * this is broken at the moment and isn't that useful anyway!
340 * chb.setSelected(minmax.get(type) != null); chb.addActionListener(new
343 * public void actionPerformed(ActionEvent e) {
344 * chb.setState(chb.getState()); if (chb.getState()) { minmax.put(type,
345 * null); } else { minmax.put(type, typeMinMax); } }
351 if (typeMinMax != null && typeMinMax[0] != null)
353 // if (table.getValueAt(row, column));
354 // graduated colourschemes for those where minmax exists for the
355 // positional features
356 final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
358 mxcol.setSelected(!featureColour.isSimpleColour());
360 mxcol.addActionListener(new ActionListener()
362 JColorChooser colorChooser;
365 public void actionPerformed(ActionEvent e)
367 if (e.getSource() == mxcol)
369 if (featureColour.isSimpleColour())
371 FeatureColourChooser fc = new FeatureColourChooser(me.fr,
373 fc.addActionListener(this);
377 // bring up simple color chooser
378 colorChooser = new JColorChooser();
379 JDialog dialog = JColorChooser.createDialog(me,
380 "Select new Colour", true, // modal
381 colorChooser, this, // OK button handler
382 null); // no CANCEL button handler
383 colorChooser.setColor(featureColour.getMaxColour());
384 dialog.setVisible(true);
389 if (e.getSource() instanceof FeatureColourChooser)
391 FeatureColourChooser fc = (FeatureColourChooser) e
393 table.setValueAt(fc.getLastColour(), selectedRow, 1);
398 // probably the color chooser!
400 new FeatureColour(colorChooser.getColor()),
403 me.updateFeatureRenderer(
404 ((FeatureTableModel) table.getModel()).getData(),
413 JMenuItem selCols = new JMenuItem(
414 MessageManager.getString("label.select_columns_containing"));
415 selCols.addActionListener(new ActionListener()
418 public void actionPerformed(ActionEvent arg0)
420 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
424 JMenuItem clearCols = new JMenuItem(
425 MessageManager.getString("label.select_columns_not_containing"));
426 clearCols.addActionListener(new ActionListener()
429 public void actionPerformed(ActionEvent arg0)
431 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
435 JMenuItem hideCols = new JMenuItem(
436 MessageManager.getString("label.hide_columns_containing"));
437 hideCols.addActionListener(new ActionListener()
440 public void actionPerformed(ActionEvent arg0)
442 fr.ap.alignFrame.hideFeatureColumns(type, true);
445 JMenuItem hideOtherCols = new JMenuItem(
446 MessageManager.getString("label.hide_columns_not_containing"));
447 hideOtherCols.addActionListener(new ActionListener()
450 public void actionPerformed(ActionEvent arg0)
452 fr.ap.alignFrame.hideFeatureColumns(type, false);
458 men.add(hideOtherCols);
459 men.show(table, x, y);
463 * true when Feature Settings are updating from feature renderer
465 private boolean handlingUpdate = false;
468 * contains a float[3] for each feature type string. created by setTableData
470 Map<String, float[]> typeWidth = null;
473 synchronized public void discoverAllFeatureData()
475 Set<String> allGroups = new HashSet<String>();
476 AlignmentI alignment = af.getViewport().getAlignment();
478 for (int i = 0; i < alignment.getHeight(); i++)
480 SequenceI seq = alignment.getSequenceAt(i);
481 for (String group : seq.getFeatures().getFeatureGroups(true))
483 if (group != null && !allGroups.contains(group))
485 allGroups.add(group);
486 checkGroupState(group);
497 * Synchronise gui group list and check visibility of group
500 * @return true if group is visible
502 private boolean checkGroupState(String group)
504 boolean visible = fr.checkGroupVisibility(group, true);
506 if (groupPanel == null)
508 groupPanel = new JPanel();
511 boolean alreadyAdded = false;
512 for (int g = 0; g < groupPanel.getComponentCount(); g++)
514 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
517 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
527 final String grp = group;
528 final JCheckBox check = new JCheckBox(group, visible);
529 check.setFont(new Font("Serif", Font.BOLD, 12));
530 check.addItemListener(new ItemListener()
533 public void itemStateChanged(ItemEvent evt)
535 fr.setGroupVisibility(check.getText(), check.isSelected());
536 af.alignPanel.getSeqPanel().seqCanvas.repaint();
537 if (af.alignPanel.overviewPanel != null)
539 af.alignPanel.overviewPanel.updateOverviewImage();
542 resetTable(new String[] { grp });
545 groupPanel.add(check);
549 boolean resettingTable = false;
551 synchronized void resetTable(String[] groupChanged)
557 resettingTable = true;
558 typeWidth = new Hashtable<String, float[]>();
559 // TODO: change avWidth calculation to 'per-sequence' average and use long
561 float[] avWidth = null;
562 SequenceFeature[] tmpfeatures;
563 String group = null, type;
564 Vector<String> visibleChecks = new Vector<String>();
566 // Find out which features should be visible depending on which groups
567 // are selected / deselected
568 // and recompute average width ordering
569 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
572 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
573 tmpfeatures = seq.getSequenceFeatures();
574 if (tmpfeatures == null)
578 Set<String> types = seq.getFeatures().getFeatureTypes();
580 while (index < tmpfeatures.length)
582 group = tmpfeatures[index].featureGroup;
584 if (tmpfeatures[index].begin == 0 && tmpfeatures[index].end == 0)
590 if (group == null || checkGroupState(group))
592 type = tmpfeatures[index].getType();
593 if (!visibleChecks.contains(type))
595 visibleChecks.addElement(type);
598 if (!typeWidth.containsKey(tmpfeatures[index].getType()))
600 typeWidth.put(tmpfeatures[index].getType(),
601 avWidth = new float[3]);
605 avWidth = typeWidth.get(tmpfeatures[index].getType());
608 if (tmpfeatures[index].getBegin() > tmpfeatures[index].getEnd())
610 avWidth[1] += 1 + tmpfeatures[index].getBegin()
611 - tmpfeatures[index].getEnd();
615 avWidth[1] += 1 + tmpfeatures[index].getEnd()
616 - tmpfeatures[index].getBegin();
622 int fSize = visibleChecks.size();
623 Object[][] data = new Object[fSize][3];
626 if (fr.hasRenderOrder())
630 fr.findAllFeatures(groupChanged != null); // prod to update
631 // colourschemes. but don't
633 // First add the checks in the previous render order,
634 // in case the window has been closed and reopened
636 List<String> frl = fr.getRenderOrder();
637 for (int ro = frl.size() - 1; ro > -1; ro--)
641 if (!visibleChecks.contains(type))
646 data[dataIndex][0] = type;
647 data[dataIndex][1] = fr.getFeatureStyle(type);
648 data[dataIndex][2] = new Boolean(af.getViewport()
649 .getFeaturesDisplayed().isVisible(type));
651 visibleChecks.removeElement(type);
655 fSize = visibleChecks.size();
656 for (int i = 0; i < fSize; i++)
658 // These must be extra features belonging to the group
659 // which was just selected
660 type = visibleChecks.elementAt(i).toString();
661 data[dataIndex][0] = type;
663 data[dataIndex][1] = fr.getFeatureStyle(type);
664 if (data[dataIndex][1] == null)
666 // "Colour has been updated in another view!!"
667 fr.clearRenderOrder();
671 data[dataIndex][2] = new Boolean(true);
675 if (originalData == null)
677 originalData = new Object[data.length][3];
678 for (int i = 0; i < data.length; i++)
680 System.arraycopy(data[i], 0, originalData[i], 0, 3);
684 table.setModel(new FeatureTableModel(data));
685 table.getColumnModel().getColumn(0).setPreferredWidth(200);
687 if (groupPanel != null)
689 groupPanel.setLayout(new GridLayout(
690 fr.getFeatureGroupsSize() / 4 + 1, 4));
692 groupPanel.validate();
693 bigPanel.add(groupPanel, BorderLayout.NORTH);
696 updateFeatureRenderer(data, groupChanged != null);
697 resettingTable = false;
701 * reorder data based on the featureRenderers global priority list.
705 private void ensureOrder(Object[][] data)
707 boolean sort = false;
708 float[] order = new float[data.length];
709 for (int i = 0; i < order.length; i++)
711 order[i] = fr.getOrder(data[i][0].toString());
714 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
718 sort = sort || order[i - 1] > order[i];
723 jalview.util.QuickSort.sort(order, data);
729 JalviewFileChooser chooser = new JalviewFileChooser("fc",
730 "Sequence Feature Colours");
731 chooser.setFileView(new JalviewFileView());
732 chooser.setDialogTitle(MessageManager
733 .getString("label.load_feature_colours"));
734 chooser.setToolTipText(MessageManager.getString("action.load"));
736 int value = chooser.showOpenDialog(this);
738 if (value == JalviewFileChooser.APPROVE_OPTION)
740 File file = chooser.getSelectedFile();
744 InputStreamReader in = new InputStreamReader(new FileInputStream(
747 JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
749 for (int i = jucs.getColourCount() - 1; i >= 0; i--)
752 jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
755 Color mincol = null, maxcol = null;
758 mincol = new Color(Integer.parseInt(newcol.getMinRGB(), 16));
759 maxcol = new Color(Integer.parseInt(newcol.getRGB(), 16));
761 } catch (Exception e)
763 Cache.log.warn("Couldn't parse out graduated feature color.",
766 FeatureColourI gcol = new FeatureColour(mincol, maxcol,
767 newcol.getMin(), newcol.getMax());
768 if (newcol.hasAutoScale())
770 gcol.setAutoScaled(newcol.getAutoScale());
772 if (newcol.hasColourByLabel())
774 gcol.setColourByLabel(newcol.getColourByLabel());
776 if (newcol.hasThreshold())
778 gcol.setThreshold(newcol.getThreshold());
780 if (newcol.getThreshType().length() > 0)
782 String ttyp = newcol.getThreshType();
783 if (ttyp.equalsIgnoreCase("ABOVE"))
785 gcol.setAboveThreshold(true);
787 if (ttyp.equalsIgnoreCase("BELOW"))
789 gcol.setBelowThreshold(true);
792 fr.setColour(name = newcol.getName(), gcol);
796 Color color = new Color(
797 Integer.parseInt(jucs.getColour(i).getRGB(), 16));
798 fr.setColour(name = jucs.getColour(i).getName(),
799 new FeatureColour(color));
801 fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
806 Object[][] data = ((FeatureTableModel) table.getModel())
809 updateFeatureRenderer(data, false);
812 } catch (Exception ex)
814 System.out.println("Error loading User Colour File\n" + ex);
821 JalviewFileChooser chooser = new JalviewFileChooser("fc",
822 "Sequence Feature Colours");
823 chooser.setFileView(new JalviewFileView());
824 chooser.setDialogTitle(MessageManager
825 .getString("label.save_feature_colours"));
826 chooser.setToolTipText(MessageManager.getString("action.save"));
828 int value = chooser.showSaveDialog(this);
830 if (value == JalviewFileChooser.APPROVE_OPTION)
832 String choice = chooser.getSelectedFile().getPath();
833 jalview.schemabinding.version2.JalviewUserColours ucs = new jalview.schemabinding.version2.JalviewUserColours();
834 ucs.setSchemeName("Sequence Features");
837 PrintWriter out = new PrintWriter(new OutputStreamWriter(
838 new FileOutputStream(choice), "UTF-8"));
840 Set<String> fr_colours = fr.getAllFeatureColours();
841 Iterator<String> e = fr_colours.iterator();
842 float[] sortOrder = new float[fr_colours.size()];
843 String[] sortTypes = new String[fr_colours.size()];
847 sortTypes[i] = e.next();
848 sortOrder[i] = fr.getOrder(sortTypes[i]);
851 QuickSort.sort(sortOrder, sortTypes);
853 for (i = 0; i < sortTypes.length; i++)
855 jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
856 col.setName(sortTypes[i]);
857 FeatureColourI fcol = fr.getFeatureStyle(sortTypes[i]);
858 if (fcol.isSimpleColour())
860 col.setRGB(Format.getHexString(fcol.getColour()));
864 col.setRGB(Format.getHexString(fcol.getMaxColour()));
865 col.setMin(fcol.getMin());
866 col.setMax(fcol.getMax());
867 col.setMinRGB(jalview.util.Format.getHexString(fcol
869 col.setAutoScale(fcol.isAutoScaled());
870 col.setThreshold(fcol.getThreshold());
871 col.setColourByLabel(fcol.isColourByLabel());
872 col.setThreshType(fcol.isAboveThreshold() ? "ABOVE" : (fcol
873 .isBelowThreshold() ? "BELOW" : "NONE"));
879 } catch (Exception ex)
881 ex.printStackTrace();
886 public void invertSelection()
888 for (int i = 0; i < table.getRowCount(); i++)
890 Boolean value = (Boolean) table.getValueAt(i, 2);
892 table.setValueAt(new Boolean(!value.booleanValue()), i, 2);
896 public void orderByAvWidth()
898 if (table == null || table.getModel() == null)
902 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
903 float[] width = new float[data.length];
907 for (int i = 0; i < data.length; i++)
909 awidth = typeWidth.get(data[i][0]);
912 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
913 // weight - but have to make per
914 // sequence, too (awidth[2])
915 // if (width[i]==1) // hack to distinguish single width sequences.
927 boolean sort = false;
928 for (int i = 0; i < width.length; i++)
930 // awidth = (float[]) typeWidth.get(data[i][0]);
933 width[i] = fr.getOrder(data[i][0].toString());
936 width[i] = fr.setOrder(data[i][0].toString(), i / data.length);
941 width[i] /= max; // normalize
942 fr.setOrder(data[i][0].toString(), width[i]); // store for later
946 sort = sort || width[i - 1] > width[i];
951 jalview.util.QuickSort.sort(width, data);
952 // update global priority order
955 updateFeatureRenderer(data, false);
963 frame.setClosed(true);
964 } catch (Exception exe)
970 public void updateFeatureRenderer(Object[][] data)
972 updateFeatureRenderer(data, true);
976 * Update the priority order of features; only repaint if this changed the
977 * order of visible features
982 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
984 if (fr.setFeaturePriority(data, visibleNew))
986 af.alignPanel.paintAlignment(true);
990 int selectedRow = -1;
992 JTabbedPane tabbedPane = new JTabbedPane();
994 BorderLayout borderLayout1 = new BorderLayout();
996 BorderLayout borderLayout2 = new BorderLayout();
998 BorderLayout borderLayout3 = new BorderLayout();
1000 JPanel bigPanel = new JPanel();
1002 BorderLayout borderLayout4 = new BorderLayout();
1004 JButton invert = new JButton();
1006 JPanel buttonPanel = new JPanel();
1008 JButton cancel = new JButton();
1010 JButton ok = new JButton();
1012 JButton loadColours = new JButton();
1014 JButton saveColours = new JButton();
1016 JPanel dasButtonPanel = new JPanel();
1018 JButton fetchDAS = new JButton();
1020 JButton saveDAS = new JButton();
1022 JButton cancelDAS = new JButton();
1024 JButton optimizeOrder = new JButton();
1026 JButton sortByScore = new JButton();
1028 JButton sortByDens = new JButton();
1030 JButton help = new JButton();
1032 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1034 private void jbInit() throws Exception
1036 this.setLayout(borderLayout1);
1037 settingsPane.setLayout(borderLayout2);
1038 dasSettingsPane.setLayout(borderLayout3);
1039 bigPanel.setLayout(borderLayout4);
1040 invert.setFont(JvSwingUtils.getLabelFont());
1041 invert.setText(MessageManager.getString("label.invert_selection"));
1042 invert.addActionListener(new ActionListener()
1045 public void actionPerformed(ActionEvent e)
1050 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1051 optimizeOrder.setText(MessageManager.getString("label.optimise_order"));
1052 optimizeOrder.addActionListener(new ActionListener()
1055 public void actionPerformed(ActionEvent e)
1060 sortByScore.setFont(JvSwingUtils.getLabelFont());
1062 .setText(MessageManager.getString("label.seq_sort_by_score"));
1063 sortByScore.addActionListener(new ActionListener()
1066 public void actionPerformed(ActionEvent e)
1068 af.avc.sortAlignmentByFeatureScore(null);
1071 sortByDens.setFont(JvSwingUtils.getLabelFont());
1072 sortByDens.setText(MessageManager
1073 .getString("label.sequence_sort_by_density"));
1074 sortByDens.addActionListener(new ActionListener()
1077 public void actionPerformed(ActionEvent e)
1079 af.avc.sortAlignmentByFeatureDensity(null);
1082 help.setFont(JvSwingUtils.getLabelFont());
1083 help.setText(MessageManager.getString("action.help"));
1084 help.addActionListener(new ActionListener()
1087 public void actionPerformed(ActionEvent e)
1091 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1092 } catch (HelpSetException e1)
1094 e1.printStackTrace();
1098 help.setFont(JvSwingUtils.getLabelFont());
1099 help.setText(MessageManager.getString("action.help"));
1100 help.addActionListener(new ActionListener()
1103 public void actionPerformed(ActionEvent e)
1107 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1108 } catch (HelpSetException e1)
1110 e1.printStackTrace();
1114 cancel.setFont(JvSwingUtils.getLabelFont());
1115 cancel.setText(MessageManager.getString("action.cancel"));
1116 cancel.addActionListener(new ActionListener()
1119 public void actionPerformed(ActionEvent e)
1121 fr.setTransparency(originalTransparency);
1122 updateFeatureRenderer(originalData);
1126 ok.setFont(JvSwingUtils.getLabelFont());
1127 ok.setText(MessageManager.getString("action.ok"));
1128 ok.addActionListener(new ActionListener()
1131 public void actionPerformed(ActionEvent e)
1136 loadColours.setFont(JvSwingUtils.getLabelFont());
1137 loadColours.setText(MessageManager.getString("label.load_colours"));
1138 loadColours.addActionListener(new ActionListener()
1141 public void actionPerformed(ActionEvent e)
1146 saveColours.setFont(JvSwingUtils.getLabelFont());
1147 saveColours.setText(MessageManager.getString("label.save_colours"));
1148 saveColours.addActionListener(new ActionListener()
1151 public void actionPerformed(ActionEvent e)
1156 transparency.addChangeListener(new ChangeListener()
1159 public void stateChanged(ChangeEvent evt)
1161 fr.setTransparency((100 - transparency.getValue()) / 100f);
1162 af.alignPanel.paintAlignment(true);
1166 transparency.setMaximum(70);
1167 transparency.setToolTipText(MessageManager
1168 .getString("label.transparency_tip"));
1169 fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
1170 fetchDAS.addActionListener(new ActionListener()
1173 public void actionPerformed(ActionEvent e)
1175 fetchDAS_actionPerformed(e);
1178 saveDAS.setText(MessageManager.getString("action.save_as_default"));
1179 saveDAS.addActionListener(new ActionListener()
1182 public void actionPerformed(ActionEvent e)
1184 saveDAS_actionPerformed(e);
1187 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1188 dasSettingsPane.setBorder(null);
1189 cancelDAS.setEnabled(false);
1190 cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1191 cancelDAS.addActionListener(new ActionListener()
1194 public void actionPerformed(ActionEvent e)
1196 cancelDAS_actionPerformed(e);
1199 this.add(tabbedPane, java.awt.BorderLayout.CENTER);
1200 tabbedPane.addTab(MessageManager.getString("label.feature_settings"),
1202 tabbedPane.addTab(MessageManager.getString("label.das_settings"),
1204 bigPanel.add(transPanel, java.awt.BorderLayout.SOUTH);
1205 transbuttons.add(optimizeOrder);
1206 transbuttons.add(invert);
1207 transbuttons.add(sortByScore);
1208 transbuttons.add(sortByDens);
1209 transbuttons.add(help);
1210 JPanel sliderPanel = new JPanel();
1211 sliderPanel.add(transparency);
1212 transPanel.add(transparency);
1213 transPanel.add(transbuttons);
1214 buttonPanel.add(ok);
1215 buttonPanel.add(cancel);
1216 buttonPanel.add(loadColours);
1217 buttonPanel.add(saveColours);
1218 bigPanel.add(scrollPane, java.awt.BorderLayout.CENTER);
1219 dasSettingsPane.add(dasButtonPanel, java.awt.BorderLayout.SOUTH);
1220 dasButtonPanel.add(fetchDAS);
1221 dasButtonPanel.add(cancelDAS);
1222 dasButtonPanel.add(saveDAS);
1223 settingsPane.add(bigPanel, java.awt.BorderLayout.CENTER);
1224 settingsPane.add(buttonPanel, java.awt.BorderLayout.SOUTH);
1227 public void fetchDAS_actionPerformed(ActionEvent e)
1229 fetchDAS.setEnabled(false);
1230 cancelDAS.setEnabled(true);
1231 dassourceBrowser.setGuiEnabled(false);
1232 Vector<jalviewSourceI> selectedSources = dassourceBrowser
1233 .getSelectedSources();
1234 doDasFeatureFetch(selectedSources, true, true);
1238 * get the features from selectedSources for all or the current selection
1240 * @param selectedSources
1241 * @param checkDbRefs
1242 * @param promptFetchDbRefs
1244 private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1245 boolean checkDbRefs, boolean promptFetchDbRefs)
1247 SequenceI[] dataset, seqs;
1249 AlignmentViewport vp = af.getViewport();
1250 if (vp.getSelectionGroup() != null
1251 && vp.getSelectionGroup().getSize() > 0)
1253 iSize = vp.getSelectionGroup().getSize();
1254 dataset = new SequenceI[iSize];
1255 seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1259 iSize = vp.getAlignment().getHeight();
1260 seqs = vp.getAlignment().getSequencesArray();
1263 dataset = new SequenceI[iSize];
1264 for (int i = 0; i < iSize; i++)
1266 dataset[i] = seqs[i].getDatasetSequence();
1269 cancelDAS.setEnabled(true);
1270 dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1271 this, selectedSources, checkDbRefs, promptFetchDbRefs);
1272 af.getViewport().setShowSequenceFeatures(true);
1273 af.showSeqFeatures.setSelected(true);
1277 * blocking call to initialise the das source browser
1279 public void initDasSources()
1281 dassourceBrowser.initDasSources();
1285 * examine the current list of das sources and return any matching the given
1286 * nicknames in sources
1289 * Vector of Strings to resolve to DAS source nicknames.
1290 * @return sources that are present in source list.
1292 public List<jalviewSourceI> resolveSourceNicknames(Vector<String> sources)
1294 return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1298 * get currently selected das sources. ensure you have called initDasSources
1299 * before calling this.
1301 * @return vector of selected das source nicknames
1303 public Vector<jalviewSourceI> getSelectedSources()
1305 return dassourceBrowser.getSelectedSources();
1309 * properly initialise DAS fetcher and then initiate a new thread to fetch
1310 * features from the named sources (rather than any turned on by default)
1314 * if true then runs in same thread, otherwise passes to the Swing
1317 public void fetchDasFeatures(Vector<String> sources, boolean block)
1320 List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1321 .resolveSourceNicknames(sources);
1322 if (resolved.size() == 0)
1324 resolved = dassourceBrowser.getSelectedSources();
1326 if (resolved.size() > 0)
1328 final List<jalviewSourceI> dassources = resolved;
1329 fetchDAS.setEnabled(false);
1330 // cancelDAS.setEnabled(true); doDasFetch does this.
1331 Runnable fetcher = new Runnable()
1337 doDasFeatureFetch(dassources, true, false);
1347 SwingUtilities.invokeLater(fetcher);
1352 public void saveDAS_actionPerformed(ActionEvent e)
1355 .saveProperties(jalview.bin.Cache.applicationProperties);
1358 public void complete()
1360 fetchDAS.setEnabled(true);
1361 cancelDAS.setEnabled(false);
1362 dassourceBrowser.setGuiEnabled(true);
1366 public void cancelDAS_actionPerformed(ActionEvent e)
1368 if (dasFeatureFetcher != null)
1370 dasFeatureFetcher.cancel();
1375 public void noDasSourceActive()
1379 .showInternalConfirmDialog(
1382 .getString("label.no_das_sources_selected_warn"),
1384 .getString("label.no_das_sources_selected_title"),
1385 JvOptionPane.DEFAULT_OPTION,
1386 JvOptionPane.INFORMATION_MESSAGE);
1389 // ///////////////////////////////////////////////////////////////////////
1390 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1391 // ///////////////////////////////////////////////////////////////////////
1392 class FeatureTableModel extends AbstractTableModel
1394 FeatureTableModel(Object[][] data)
1399 private String[] columnNames = {
1400 MessageManager.getString("label.feature_type"),
1401 MessageManager.getString("action.colour"),
1402 MessageManager.getString("label.display") };
1404 private Object[][] data;
1406 public Object[][] getData()
1411 public void setData(Object[][] data)
1417 public int getColumnCount()
1419 return columnNames.length;
1422 public Object[] getRow(int row)
1428 public int getRowCount()
1434 public String getColumnName(int col)
1436 return columnNames[col];
1440 public Object getValueAt(int row, int col)
1442 return data[row][col];
1446 public Class getColumnClass(int c)
1448 return getValueAt(0, c).getClass();
1452 public boolean isCellEditable(int row, int col)
1454 return col == 0 ? false : true;
1458 public void setValueAt(Object value, int row, int col)
1460 data[row][col] = value;
1461 fireTableCellUpdated(row, col);
1462 updateFeatureRenderer(data);
1467 class ColorRenderer extends JLabel implements TableCellRenderer
1469 javax.swing.border.Border unselectedBorder = null;
1471 javax.swing.border.Border selectedBorder = null;
1473 final String baseTT = "Click to edit, right/apple click for menu.";
1475 public ColorRenderer()
1477 setOpaque(true); // MUST do this for background to show up.
1478 setHorizontalTextPosition(SwingConstants.CENTER);
1479 setVerticalTextPosition(SwingConstants.CENTER);
1483 public Component getTableCellRendererComponent(JTable tbl,
1484 Object color, boolean isSelected, boolean hasFocus, int row,
1487 FeatureColourI cellColour = (FeatureColourI) color;
1488 // JLabel comp = new JLabel();
1492 // setBounds(getBounds());
1494 setToolTipText(baseTT);
1495 setBackground(tbl.getBackground());
1496 if (!cellColour.isSimpleColour())
1498 Rectangle cr = tbl.getCellRect(row, column, false);
1499 FeatureSettings.renderGraduatedColor(this, cellColour,
1500 (int) cr.getWidth(), (int) cr.getHeight());
1507 newColor = cellColour.getColour();
1508 setBackground(newColor);
1512 if (selectedBorder == null)
1514 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1515 tbl.getSelectionBackground());
1517 setBorder(selectedBorder);
1521 if (unselectedBorder == null)
1523 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1524 tbl.getBackground());
1526 setBorder(unselectedBorder);
1534 * update comp using rendering settings from gcol
1539 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1541 int w = comp.getWidth(), h = comp.getHeight();
1544 w = (int) comp.getPreferredSize().getWidth();
1545 h = (int) comp.getPreferredSize().getHeight();
1552 renderGraduatedColor(comp, gcol, w, h);
1555 public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1558 boolean thr = false;
1561 if (gcol.isAboveThreshold())
1565 tt += "Thresholded (Above " + gcol.getThreshold() + ") ";
1567 if (gcol.isBelowThreshold())
1571 tt += "Thresholded (Below " + gcol.getThreshold() + ") ";
1573 if (gcol.isColourByLabel())
1575 tt = "Coloured by label text. " + tt;
1585 Color newColor = gcol.getMaxColour();
1586 comp.setBackground(newColor);
1587 // System.err.println("Width is " + w / 2);
1588 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1589 comp.setIcon(ficon);
1590 // tt+="RGB value: Max (" + newColor.getRed() + ", "
1591 // + newColor.getGreen() + ", " + newColor.getBlue()
1592 // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1593 // + ", " + minCol.getBlue() + ")");
1595 comp.setHorizontalAlignment(SwingConstants.CENTER);
1597 if (tt.length() > 0)
1599 if (comp.getToolTipText() == null)
1601 comp.setToolTipText(tt);
1605 comp.setToolTipText(tt + " " + comp.getToolTipText());
1611 class FeatureIcon implements Icon
1613 FeatureColourI gcol;
1617 boolean midspace = false;
1619 int width = 50, height = 20;
1621 int s1, e1; // start and end of midpoint band for thresholded symbol
1623 Color mpcolour = Color.white;
1625 FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
1645 public int getIconWidth()
1651 public int getIconHeight()
1657 public void paintIcon(Component c, Graphics g, int x, int y)
1660 if (gcol.isColourByLabel())
1663 g.fillRect(0, 0, width, height);
1664 // need an icon here.
1665 g.setColor(gcol.getMaxColour());
1667 g.setFont(new Font("Verdana", Font.PLAIN, 9));
1669 // g.setFont(g.getFont().deriveFont(
1670 // AffineTransform.getScaleInstance(
1671 // width/g.getFontMetrics().stringWidth("Label"),
1672 // height/g.getFontMetrics().getHeight())));
1674 g.drawString(MessageManager.getString("label.label"), 0, 0);
1679 Color minCol = gcol.getMinColour();
1681 g.fillRect(0, 0, s1, height);
1684 g.setColor(Color.white);
1685 g.fillRect(s1, 0, e1 - s1, height);
1687 g.setColor(gcol.getMaxColour());
1688 g.fillRect(0, e1, width - e1, height);
1693 class ColorEditor extends AbstractCellEditor implements TableCellEditor,
1698 FeatureColourI currentColor;
1700 FeatureColourChooser chooser;
1706 JColorChooser colorChooser;
1710 protected static final String EDIT = "edit";
1712 int selectedRow = 0;
1714 public ColorEditor(FeatureSettings me)
1717 // Set up the editor (from the table's point of view),
1718 // which is a button.
1719 // This button brings up the color chooser dialog,
1720 // which is the editor from the user's point of view.
1721 button = new JButton();
1722 button.setActionCommand(EDIT);
1723 button.addActionListener(this);
1724 button.setBorderPainted(false);
1725 // Set up the dialog that the button brings up.
1726 colorChooser = new JColorChooser();
1727 dialog = JColorChooser.createDialog(button, "Select new Colour", true, // modal
1728 colorChooser, this, // OK button handler
1729 null); // no CANCEL button handler
1733 * Handles events from the editor button and from the dialog's OK button.
1736 public void actionPerformed(ActionEvent e)
1739 if (EDIT.equals(e.getActionCommand()))
1741 // The user has clicked the cell, so
1742 // bring up the dialog.
1743 if (currentColor.isSimpleColour())
1745 // bring up simple color chooser
1746 button.setBackground(currentColor.getColour());
1747 colorChooser.setColor(currentColor.getColour());
1748 dialog.setVisible(true);
1752 // bring up graduated chooser.
1753 chooser = new FeatureColourChooser(me.fr, type);
1754 chooser.setRequestFocusEnabled(true);
1755 chooser.requestFocus();
1756 chooser.addActionListener(this);
1758 // Make the renderer reappear.
1759 fireEditingStopped();
1763 { // User pressed dialog's "OK" button.
1764 if (currentColor.isSimpleColour())
1766 currentColor = new FeatureColour(colorChooser.getColor());
1770 currentColor = chooser.getLastColour();
1772 me.table.setValueAt(getCellEditorValue(), selectedRow, 1);
1773 fireEditingStopped();
1774 me.table.validate();
1778 // Implement the one CellEditor method that AbstractCellEditor doesn't.
1780 public Object getCellEditorValue()
1782 return currentColor;
1785 // Implement the one method defined by TableCellEditor.
1787 public Component getTableCellEditorComponent(JTable table, Object value,
1788 boolean isSelected, int row, int column)
1790 currentColor = (FeatureColourI) value;
1791 this.selectedRow = row;
1792 type = me.table.getValueAt(row, 0).toString();
1793 button.setOpaque(true);
1794 button.setBackground(me.getBackground());
1795 if (!currentColor.isSimpleColour())
1797 JLabel btn = new JLabel();
1798 btn.setSize(button.getSize());
1799 FeatureSettings.renderGraduatedColor(btn, currentColor);
1800 button.setBackground(btn.getBackground());
1801 button.setIcon(btn.getIcon());
1802 button.setText(btn.getText());
1807 button.setIcon(null);
1808 button.setBackground(currentColor.getColour());