2 * Jalview - A Sequence Alignment Editor and Viewer
3 * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 import java.awt.event.*;
26 import java.beans.PropertyChangeEvent;
27 import java.beans.PropertyChangeListener;
30 import javax.swing.event.*;
31 import javax.swing.table.*;
33 import jalview.datamodel.*;
36 public class FeatureSettings
39 DasSourceBrowser dassourceBrowser;
40 jalview.io.DasSequenceFeatureFetcher dasFeatureFetcher;
41 JPanel settingsPane = new JPanel();
42 JPanel dasSettingsPane = new JPanel();
44 final FeatureRenderer fr;
45 public final AlignFrame af;
46 Object[][] originalData;
47 final JInternalFrame frame;
48 JScrollPane scrollPane = new JScrollPane();
51 JSlider transparency = new JSlider();
53 JPanel transPanel = new JPanel(new FlowLayout());
55 public FeatureSettings(AlignFrame af)
58 fr = af.getFeatureRenderer();
60 transparency.setMaximum(100 - (int) (fr.transparency * 100));
72 table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
73 table.setFont(new Font("Verdana", Font.PLAIN, 12));
74 table.setDefaultRenderer(Color.class,
77 table.setDefaultEditor(Color.class,
80 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
82 table.addMouseListener(new MouseAdapter()
84 public void mousePressed(MouseEvent evt)
86 selectedRow = table.rowAtPoint(evt.getPoint());
90 table.addMouseMotionListener(new MouseMotionAdapter()
92 public void mouseDragged(MouseEvent evt)
94 int newRow = table.rowAtPoint(evt.getPoint());
95 if (newRow != selectedRow
99 Object[] temp = new Object[3];
100 temp[0] = table.getValueAt(selectedRow, 0);
101 temp[1] = table.getValueAt(selectedRow, 1);
102 temp[2] = table.getValueAt(selectedRow, 2);
104 table.setValueAt(table.getValueAt(newRow, 0), selectedRow, 0);
105 table.setValueAt(table.getValueAt(newRow, 1), selectedRow, 1);
106 table.setValueAt(table.getValueAt(newRow, 2), selectedRow, 2);
108 table.setValueAt(temp[0], newRow, 0);
109 table.setValueAt(temp[1], newRow, 1);
110 table.setValueAt(temp[2], newRow, 2);
112 selectedRow = newRow;
117 scrollPane.setViewportView(table);
119 dassourceBrowser = new DasSourceBrowser();
120 dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
122 if (af.getViewport().featuresDisplayed == null || fr.renderOrder == null)
124 fr.findAllFeatures(true); // display everything!
128 final PropertyChangeListener change;
129 final FeatureSettings fs=this;
130 fr.addPropertyChangeListener(change=new PropertyChangeListener() {
131 private boolean firing=false;
132 public void propertyChange(PropertyChangeEvent evt)
134 if (!fs.resettingTable && !firing) {
143 frame = new JInternalFrame();
144 frame.setContentPane(this);
145 Desktop.addInternalFrame(frame, "Sequence Feature Settings", 400, 450);
146 frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
148 public void internalFrameClosed(
149 javax.swing.event.InternalFrameEvent evt)
151 fr.removePropertyChangeListener(change);
155 frame.setLayer(JLayeredPane.PALETTE_LAYER);
158 * contains a float[3] for each feature type string. created by setTableData
160 Hashtable typeWidth=null;
161 synchronized public void setTableData()
163 if (fr.featureGroups == null)
165 fr.featureGroups = new Hashtable();
167 Vector allFeatures = new Vector();
168 Vector allGroups = new Vector();
169 SequenceFeature[] tmpfeatures;
171 for (int i = 0; i < af.getViewport().alignment.getHeight(); i++)
173 if (af.getViewport().alignment.getSequenceAt(i).getDatasetSequence().
174 getSequenceFeatures() == null)
179 tmpfeatures = af.getViewport().alignment.getSequenceAt(i).
180 getDatasetSequence().getSequenceFeatures();
183 while (index < tmpfeatures.length)
185 if (tmpfeatures[index].begin == 0 && tmpfeatures[index].end == 0)
191 if (tmpfeatures[index].getFeatureGroup() != null)
193 group = tmpfeatures[index].featureGroup;
194 if (!allGroups.contains(group))
196 allGroups.addElement(group);
199 if (fr.featureGroups.containsKey(group))
201 visible = ( (Boolean) fr.featureGroups.get(group)).booleanValue();
203 visible=true; //initGroups || fr.av.featuresDisplayed.containsKey(tmpfeatures[index].getType());
206 if (groupPanel == null)
208 groupPanel = new JPanel();
211 boolean alreadyAdded = false;
212 for (int g = 0; g < groupPanel.getComponentCount(); g++)
214 if ( ( (JCheckBox) groupPanel.getComponent(g))
215 .getText().equals(group))
228 fr.featureGroups.put(group, new Boolean(visible));
229 final String grp = group;
230 final JCheckBox check = new JCheckBox(group, visible);
231 check.setFont(new Font("Serif", Font.BOLD, 12));
232 check.addItemListener(new ItemListener()
234 public void itemStateChanged(ItemEvent evt)
236 fr.featureGroups.put(check.getText(),
237 new Boolean(check.isSelected()));
238 af.alignPanel.seqPanel.seqCanvas.repaint();
239 if (af.alignPanel.overviewPanel != null)
241 af.alignPanel.overviewPanel.updateOverviewImage();
244 resetTable(new String[] { grp } );
247 groupPanel.add(check);
251 if (!allFeatures.contains(tmpfeatures[index].getType()))
253 allFeatures.addElement(tmpfeatures[index].getType());
263 boolean resettingTable=false;
264 void resetTable(String[] groupChanged)
267 typeWidth=new Hashtable();
268 Vector groupchanged = new Vector();
269 // TODO: change avWidth calculation to 'per-sequence' average and use long rather than float
270 float[] avWidth=null;
271 SequenceFeature[] tmpfeatures;
272 String group = null, type;
273 Vector visibleChecks = new Vector();
275 //Find out which features should be visible depending on which groups
276 //are selected / deselected
277 //and recompute average width ordering
278 for (int i = 0; i < af.getViewport().alignment.getHeight(); i++)
281 tmpfeatures = af.getViewport().alignment.getSequenceAt(i).
282 getDatasetSequence().getSequenceFeatures();
283 if (tmpfeatures == null)
289 while (index < tmpfeatures.length)
291 group = tmpfeatures[index].featureGroup;
293 if (tmpfeatures[index].begin == 0 && tmpfeatures[index].end == 0)
299 if (group == null || fr.featureGroups.get(group) == null ||
300 ( (Boolean) fr.featureGroups.get(group)).booleanValue())
302 type = tmpfeatures[index].getType();
303 if (groupchanged==null || groupchanged.contains(group)) {
304 //af.getViewport().featuresDisplayed.put(type, fr.getColour(type));
306 if (!visibleChecks.contains(type))
308 visibleChecks.addElement(type);
311 if (!typeWidth.containsKey(tmpfeatures[index].getType())) {
312 typeWidth.put(tmpfeatures[index].getType(), avWidth=new float[3]);
314 avWidth = (float[]) typeWidth.get(tmpfeatures[index].getType());
317 if (tmpfeatures[index].getBegin()>tmpfeatures[index].getEnd())
319 avWidth[1]+=1+tmpfeatures[index].getBegin()-tmpfeatures[index].getEnd();
321 avWidth[1]+=1+tmpfeatures[index].getEnd()-tmpfeatures[index].getBegin();
327 int fSize = visibleChecks.size();
328 Object[][] data = new Object[fSize][3];
331 if (fr.renderOrder != null)
333 fr.findAllFeatures(groupChanged!=null); // prod to update colourschemes. but don't affect display
334 //First add the checks in the previous render order,
335 //in case the window has been closed and reopened
336 for (int ro = fr.renderOrder.length - 1; ro > -1; ro--)
338 type = fr.renderOrder[ro];
340 if (!visibleChecks.contains(type))
345 data[dataIndex][0] = type;
346 data[dataIndex][1] = fr.getColour(type);
347 data[dataIndex][2] = new Boolean(af.getViewport().featuresDisplayed.containsKey(type));
349 visibleChecks.removeElement(type);
353 fSize = visibleChecks.size();
354 for (int i = 0; i < fSize; i++)
356 //These must be extra features belonging to the group
357 //which was just selected
358 type = visibleChecks.elementAt(i).toString();
359 data[dataIndex][0] = type;
361 data[dataIndex][1] = fr.getColour(type);
362 if (data[dataIndex][1] == null)
364 //"Colour has been updated in another view!!"
365 fr.renderOrder = null;
369 data[dataIndex][2] = new Boolean(true);
373 if (originalData == null)
375 originalData = new Object[data.length][3];
376 System.arraycopy(data, 0, originalData, 0, data.length);
379 table.setModel(new FeatureTableModel(data));
380 table.getColumnModel().getColumn(0).setPreferredWidth(200);
382 if (groupPanel != null)
384 groupPanel.setLayout(
385 new GridLayout(fr.featureGroups.size() / 4 + 1, 4));
387 groupPanel.validate();
388 bigPanel.add(groupPanel, BorderLayout.NORTH);
391 updateFeatureRenderer(data, groupChanged!=null);
392 resettingTable=false;
395 * reorder data based on the featureRenderers global priority list.
398 private void ensureOrder(Object[][] data)
401 float[] order = new float[data.length];
402 for (int i=0;i<order.length; i++)
404 order[i] = fr.getOrder(data[i][0].toString());
406 order[i] = fr.setOrder(data[i][0].toString(), i/order.length);
408 sort = sort || order[i-1]>order[i];
411 jalview.util.QuickSort.sort(order, data);
416 JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
418 "LAST_DIRECTORY"), new String[]
421 {"Sequence Feature Colours"}, "Sequence Feature Colours");
422 chooser.setFileView(new jalview.io.JalviewFileView());
423 chooser.setDialogTitle("Load Feature Colours");
424 chooser.setToolTipText("Load");
426 int value = chooser.showOpenDialog(this);
428 if (value == JalviewFileChooser.APPROVE_OPTION)
430 File file = chooser.getSelectedFile();
434 InputStreamReader in = new InputStreamReader(new FileInputStream(
437 jalview.binding.JalviewUserColours jucs = new jalview.binding.
438 JalviewUserColours();
439 jucs = (jalview.binding.JalviewUserColours) jucs.unmarshal(in);
441 for (int i = 0; i < jucs.getColourCount(); i++)
444 fr.setColour(name=jucs.getColour(i).getName(),
445 new Color(Integer.parseInt(jucs.getColour(i).getRGB(),
447 fr.setOrder(name,i/jucs.getColourCount());
451 af.alignPanel.paintAlignment(true);
455 System.out.println("Error loading User Colour File\n" + ex);
462 JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
464 "LAST_DIRECTORY"), new String[]
467 {"Sequence Feature Colours"}, "Sequence Feature Colours");
468 chooser.setFileView(new jalview.io.JalviewFileView());
469 chooser.setDialogTitle("Save Feature Colour Scheme");
470 chooser.setToolTipText("Save");
472 int value = chooser.showSaveDialog(this);
474 if (value == JalviewFileChooser.APPROVE_OPTION)
476 String choice = chooser.getSelectedFile().getPath();
477 jalview.binding.JalviewUserColours ucs = new jalview.binding.
478 JalviewUserColours();
479 ucs.setSchemeName("Sequence Features");
482 PrintWriter out = new PrintWriter(new OutputStreamWriter(
483 new FileOutputStream(choice), "UTF-8"));
485 Enumeration e = fr.featureColours.keys();
486 while (e.hasMoreElements())
488 jalview.binding.Colour col = new jalview.binding.Colour();
489 col.setName(e.nextElement().toString());
490 col.setRGB(jalview.util.Format.getHexString(
491 fr.getColour(col.getName())));
500 ex.printStackTrace();
505 public void invertSelection()
507 for (int i = 0; i < table.getRowCount(); i++)
509 Boolean value = (Boolean) table.getValueAt(i, 2);
512 new Boolean(!value.booleanValue()),
516 public void orderByAvWidth() {
517 if (table==null || table.getModel()==null)
519 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
520 float[] width = new float[data.length];
524 for (int i=0;i<data.length;i++) {
525 awidth = (float[]) typeWidth.get(data[i][0]);
527 width[i] = awidth[1]/awidth[0];// *awidth[0]*awidth[2]; - better weight - but have to make per sequence, too (awidth[2])
528 //if (width[i]==1) // hack to distinguish single width sequences.
537 for (int i=0;i<width.length; i++) {
538 //awidth = (float[]) typeWidth.get(data[i][0]);
541 width[i] = fr.getOrder(data[i][0].toString());
544 width[i] = fr.setOrder(data[i][0].toString(), i/data.length);
547 width[i] /=max; // normalize
548 fr.setOrder(data[i][0].toString(), width[i]); // store for later
551 sort = sort || width[i-1]>width[i];
554 jalview.util.QuickSort.sort(width, data);
555 // update global priority order
557 updateFeatureRenderer(data,false);
564 frame.setClosed(true);
566 catch (Exception exe)
571 public void updateFeatureRenderer(Object[][] data)
573 updateFeatureRenderer(data, true);
575 private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
577 fr.setFeaturePriority(data, visibleNew);
578 af.alignPanel.paintAlignment(true);
580 if (af.alignPanel.overviewPanel != null)
582 af.alignPanel.overviewPanel.updateOverviewImage();
586 int selectedRow = -1;
587 JTabbedPane tabbedPane = new JTabbedPane();
588 BorderLayout borderLayout1 = new BorderLayout();
589 BorderLayout borderLayout2 = new BorderLayout();
590 BorderLayout borderLayout3 = new BorderLayout();
591 JPanel bigPanel = new JPanel();
592 BorderLayout borderLayout4 = new BorderLayout();
593 JButton invert = new JButton();
594 JPanel buttonPanel = new JPanel();
595 JButton cancel = new JButton();
596 JButton ok = new JButton();
597 JButton loadColours = new JButton();
598 JButton saveColours = new JButton();
599 JPanel dasButtonPanel = new JPanel();
600 JButton fetchDAS = new JButton();
601 JButton saveDAS = new JButton();
602 JButton cancelDAS = new JButton();
603 JButton optimizeOrder = new JButton();
604 JPanel transbuttons = new JPanel(new BorderLayout());
605 private void jbInit()
608 this.setLayout(borderLayout1);
609 settingsPane.setLayout(borderLayout2);
610 dasSettingsPane.setLayout(borderLayout3);
611 bigPanel.setLayout(borderLayout4);
612 invert.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
613 invert.setText("Invert Selection");
614 invert.addActionListener(new ActionListener()
616 public void actionPerformed(ActionEvent e)
621 optimizeOrder.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
622 optimizeOrder.setText("Optimise Order");
623 optimizeOrder.addActionListener(new ActionListener() {
624 public void actionPerformed(ActionEvent e) {
628 cancel.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
629 cancel.setText("Cancel");
630 cancel.addActionListener(new ActionListener()
632 public void actionPerformed(ActionEvent e)
634 updateFeatureRenderer(originalData);
638 ok.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
640 ok.addActionListener(new ActionListener()
642 public void actionPerformed(ActionEvent e)
647 loadColours.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
648 loadColours.setText("Load Colours");
649 loadColours.addActionListener(new ActionListener()
651 public void actionPerformed(ActionEvent e)
656 saveColours.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
657 saveColours.setText("Save Colours");
658 saveColours.addActionListener(new ActionListener()
660 public void actionPerformed(ActionEvent e)
665 transparency.addChangeListener(new ChangeListener()
667 public void stateChanged(ChangeEvent evt)
669 fr.setTransparency( (float) (100 - transparency.getValue()) / 100f);
670 af.alignPanel.paintAlignment(true);
674 transparency.setMaximum(70);
675 fetchDAS.setText("Fetch DAS Features");
676 fetchDAS.addActionListener(new ActionListener()
678 public void actionPerformed(ActionEvent e)
680 fetchDAS_actionPerformed(e);
683 saveDAS.setText("Save as default");
684 saveDAS.addActionListener(new ActionListener()
686 public void actionPerformed(ActionEvent e)
688 saveDAS_actionPerformed(e);
691 dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
692 dasSettingsPane.setBorder(null);
693 cancelDAS.setEnabled(false);
694 cancelDAS.setText("Cancel Fetch");
695 cancelDAS.addActionListener(new ActionListener()
697 public void actionPerformed(ActionEvent e)
699 cancelDAS_actionPerformed(e);
702 this.add(tabbedPane, java.awt.BorderLayout.CENTER);
703 tabbedPane.addTab("Feature Settings", settingsPane);
704 tabbedPane.addTab("DAS Settings", dasSettingsPane);
705 bigPanel.add(transPanel, java.awt.BorderLayout.SOUTH);
706 transPanel.add(transparency);
707 transbuttons.add(invert, java.awt.BorderLayout.NORTH);
708 transbuttons.add(optimizeOrder,java.awt.BorderLayout.SOUTH);
709 transPanel.add(transbuttons);
711 buttonPanel.add(cancel);
712 buttonPanel.add(loadColours);
713 buttonPanel.add(saveColours);
714 bigPanel.add(scrollPane, java.awt.BorderLayout.CENTER);
715 dasSettingsPane.add(dasButtonPanel, java.awt.BorderLayout.SOUTH);
716 dasButtonPanel.add(fetchDAS);
717 dasButtonPanel.add(cancelDAS);
718 dasButtonPanel.add(saveDAS);
719 settingsPane.add(bigPanel, java.awt.BorderLayout.CENTER);
720 settingsPane.add(buttonPanel, java.awt.BorderLayout.SOUTH);
723 public void fetchDAS_actionPerformed(ActionEvent e)
725 fetchDAS.setEnabled(false);
726 cancelDAS.setEnabled(true);
727 Vector selectedSources = dassourceBrowser.getSelectedSources();
729 SequenceI[] dataset, seqs;
732 if (af.getViewport().getSelectionGroup() != null
733 && af.getViewport().getSelectionGroup().getSize() > 0)
735 iSize = af.getViewport().getSelectionGroup().getSize();
736 dataset = new SequenceI[iSize];
737 seqs = af.getViewport().getSelectionGroup().
739 af.getViewport().getAlignment());
743 iSize = af.getViewport().getAlignment().getHeight();
744 seqs = af.getViewport().getAlignment().getSequencesArray();
747 dataset = new SequenceI[iSize];
748 for (int i = 0; i < iSize; i++)
750 dataset[i] = seqs[i].getDatasetSequence();
754 new jalview.io.DasSequenceFeatureFetcher(
758 cancelDAS.setEnabled(true);
759 af.getViewport().setShowSequenceFeatures(true);
760 af.showSeqFeatures.setSelected(true);
763 public void saveDAS_actionPerformed(ActionEvent e)
765 dassourceBrowser.saveProperties(jalview.bin.Cache.applicationProperties);
768 public void complete()
770 fetchDAS.setEnabled(true);
771 cancelDAS.setEnabled(false);
774 public void cancelDAS_actionPerformed(ActionEvent e)
776 dasFeatureFetcher.cancel();
777 fetchDAS.setEnabled(true);
778 cancelDAS.setEnabled(false);
781 /////////////////////////////////////////////////////////////////////////
782 // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
783 /////////////////////////////////////////////////////////////////////////
784 class FeatureTableModel
785 extends AbstractTableModel
787 FeatureTableModel(Object[][] data)
792 private String[] columnNames =
794 "Feature Type", "Colour", "Display"};
795 private Object[][] data;
797 public Object[][] getData()
802 public void setData(Object[][] data)
807 public int getColumnCount()
809 return columnNames.length;
812 public Object[] getRow(int row)
817 public int getRowCount()
822 public String getColumnName(int col)
824 return columnNames[col];
827 public Object getValueAt(int row, int col)
829 return data[row][col];
832 public Class getColumnClass(int c)
834 return getValueAt(0, c).getClass();
837 public boolean isCellEditable(int row, int col)
839 return col == 0 ? false : true;
842 public void setValueAt(Object value, int row, int col)
844 data[row][col] = value;
845 fireTableCellUpdated(row, col);
846 updateFeatureRenderer(data);
852 extends JLabel implements TableCellRenderer
854 javax.swing.border.Border unselectedBorder = null;
855 javax.swing.border.Border selectedBorder = null;
857 public ColorRenderer()
859 setOpaque(true); //MUST do this for background to show up.
862 public Component getTableCellRendererComponent(
863 JTable table, Object color,
864 boolean isSelected, boolean hasFocus,
867 Color newColor = (Color) color;
868 setBackground(newColor);
871 if (selectedBorder == null)
873 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
874 table.getSelectionBackground());
876 setBorder(selectedBorder);
880 if (unselectedBorder == null)
882 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
883 table.getBackground());
885 setBorder(unselectedBorder);
888 setToolTipText("RGB value: " + newColor.getRed() + ", "
889 + newColor.getGreen() + ", "
890 + newColor.getBlue());
897 extends AbstractCellEditor implements TableCellEditor,
902 JColorChooser colorChooser;
904 protected static final String EDIT = "edit";
908 //Set up the editor (from the table's point of view),
910 //This button brings up the color chooser dialog,
911 //which is the editor from the user's point of view.
912 button = new JButton();
913 button.setActionCommand(EDIT);
914 button.addActionListener(this);
915 button.setBorderPainted(false);
916 //Set up the dialog that the button brings up.
917 colorChooser = new JColorChooser();
918 dialog = JColorChooser.createDialog(button,
922 this, //OK button handler
923 null); //no CANCEL button handler
927 * Handles events from the editor button and from
928 * the dialog's OK button.
930 public void actionPerformed(ActionEvent e)
933 if (EDIT.equals(e.getActionCommand()))
935 //The user has clicked the cell, so
936 //bring up the dialog.
937 button.setBackground(currentColor);
938 colorChooser.setColor(currentColor);
939 dialog.setVisible(true);
941 //Make the renderer reappear.
942 fireEditingStopped();
946 { //User pressed dialog's "OK" button.
947 currentColor = colorChooser.getColor();
951 //Implement the one CellEditor method that AbstractCellEditor doesn't.
952 public Object getCellEditorValue()
957 //Implement the one method defined by TableCellEditor.
958 public Component getTableCellEditorComponent(JTable table,
964 currentColor = (Color) value;