X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FStructureViewerBase.java;h=808150fa1cbbdf746644e0a995b808b858ddabbc;hb=20bb45279db7745e7d0062c1084f97644c60db19;hp=587e08aa54a49623490ae42780aa5e3cfadc4764;hpb=2373f90e6077de127c55ec6cb7671d8ba2436684;p=jalview.git diff --git a/src/jalview/gui/StructureViewerBase.java b/src/jalview/gui/StructureViewerBase.java index 587e08a..808150f 100644 --- a/src/jalview/gui/StructureViewerBase.java +++ b/src/jalview/gui/StructureViewerBase.java @@ -1,21 +1,66 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Jalview is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ package jalview.gui; +import jalview.bin.Cache; +import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.ColumnSelection; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; import jalview.gui.StructureViewer.ViewerType; import jalview.gui.ViewSelectionMenu.ViewSetProvider; -import jalview.io.AppletFormatAdapter; +import jalview.io.DataSourceType; +import jalview.io.JalviewFileChooser; +import jalview.io.JalviewFileView; import jalview.jbgui.GStructureViewer; +import jalview.schemes.ColourSchemeI; +import jalview.schemes.ColourSchemes; import jalview.structures.models.AAStructureBindingModel; import jalview.util.MessageManager; +import java.awt.Color; import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Vector; +import javax.swing.ButtonGroup; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JColorChooser; +import javax.swing.JMenu; import javax.swing.JMenuItem; -import javax.swing.JOptionPane; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.event.MenuEvent; +import javax.swing.event.MenuListener; /** * Base class with common functionality for JMol, Chimera or other structure @@ -27,27 +72,53 @@ import javax.swing.JOptionPane; public abstract class StructureViewerBase extends GStructureViewer implements Runnable, ViewSetProvider { + /* + * names for colour options (additional to Jalview colour schemes) + */ + enum ViewerColour + { + BySequence, ByChain, ChargeCysteine, ByViewer + } /** * list of sequenceSet ids associated with the view */ protected List _aps = new ArrayList(); + /** * list of alignment panels to use for superposition */ protected Vector _alignwith = new Vector(); + /** * list of alignment panels that are used for colouring structures by aligned * sequences */ protected Vector _colourwith = new Vector(); + private String viewId = null; + private AlignmentPanel ap; + protected boolean alignAddedStructures = false; + protected boolean _started = false; + protected boolean addingStructures = false; + protected Thread worker = null; + protected boolean allChainsSelected = false; + + protected JMenu viewSelectionMenu; + + /** + * Default constructor + */ + public StructureViewerBase() + { + super(); + } /** * * @param ap2 @@ -60,7 +131,7 @@ public abstract class StructureViewerBase extends GStructureViewer public boolean isUsedforaligment(AlignmentPanel ap2) { - + return (_alignwith != null) && _alignwith.contains(ap2); } @@ -124,6 +195,7 @@ public abstract class StructureViewerBase extends GStructureViewer this.ap = alp; } + @Override public AlignmentPanel[] getAllAlignmentPanels() { AlignmentPanel[] t, list = new AlignmentPanel[0]; @@ -138,7 +210,7 @@ public abstract class StructureViewerBase extends GStructureViewer list = t; } } - + return list; } @@ -209,7 +281,8 @@ public abstract class StructureViewerBase extends GStructureViewer } } - public void useAlignmentPanelForColourbyseq(AlignmentPanel nap, boolean enableColourBySeq) + public void useAlignmentPanelForColourbyseq(AlignmentPanel nap, + boolean enableColourBySeq) { useAlignmentPanelForColourbyseq(nap); getBinding().setColourBySequence(enableColourBySeq); @@ -236,8 +309,6 @@ public abstract class StructureViewerBase extends GStructureViewer public abstract ViewerType getViewerType(); - protected abstract AAStructureBindingModel getBindingModel(); - /** * add a new structure (with associated sequences and chains) to this viewer, * retrieving it if necessary first. @@ -250,7 +321,8 @@ public abstract class StructureViewerBase extends GStructureViewer * alignment * @param alignFrame */ - protected void addStructure(final PDBEntry pdbentry, final SequenceI[] seqs, final String[] chains, + protected void addStructure(final PDBEntry pdbentry, + final SequenceI[] seqs, final String[] chains, final boolean align, final IProgressIndicator alignFrame) { if (pdbentry.getFile() == null) @@ -261,6 +333,7 @@ public abstract class StructureViewerBase extends GStructureViewer // queue. new Thread(new Runnable() { + @Override public void run() { while (worker != null && worker.isAlive() && _started) @@ -268,7 +341,7 @@ public abstract class StructureViewerBase extends GStructureViewer try { Thread.sleep(100 + ((int) Math.random() * 100)); - + } catch (Exception e) { } @@ -281,7 +354,7 @@ public abstract class StructureViewerBase extends GStructureViewer } } // otherwise, start adding the structure. - getBindingModel().addSequenceAndChain(new PDBEntry[] { pdbentry }, + getBinding().addSequenceAndChain(new PDBEntry[] { pdbentry }, new SequenceI[][] { seqs }, new String[][] { chains }); addingStructures = true; _started = false; @@ -297,20 +370,24 @@ public abstract class StructureViewerBase extends GStructureViewer * * @param pdbId * @param view - * @return YES, NO or CANCEL JOptionPane code + * @return YES, NO or CANCEL JvOptionPane code */ - protected int chooseAlignStructureToViewer(String pdbId, StructureViewerBase view) + protected int chooseAlignStructureToViewer(String pdbId, + StructureViewerBase view) { - int option = JOptionPane.showInternalConfirmDialog(Desktop.desktop, + int option = JvOptionPane.showInternalConfirmDialog(Desktop.desktop, MessageManager.formatMessage("label.add_pdbentry_to_view", - new Object[] - { pdbId, view.getTitle() }), MessageManager + new Object[] { pdbId, view.getTitle() }), + MessageManager .getString("label.align_to_existing_structure_view"), - JOptionPane.YES_NO_CANCEL_OPTION); + JvOptionPane.YES_NO_CANCEL_OPTION); return option; } - protected abstract boolean hasPdbId(String pdbId); + protected boolean hasPdbId(String pdbId) + { + return getBinding().hasPdbId(pdbId); + } protected abstract List getViewersFor( AlignmentPanel alp); @@ -341,11 +418,11 @@ public abstract class StructureViewerBase extends GStructureViewer continue; } int option = chooseAlignStructureToViewer(pdbId, view); - if (option == JOptionPane.CANCEL_OPTION) + if (option == JvOptionPane.CANCEL_OPTION) { return true; } - else if (option == JOptionPane.YES_OPTION) + else if (option == JvOptionPane.YES_OPTION) { view.useAlignmentPanelForSuperposition(apanel); view.addStructure(pdbentry, seq, chains, true, apanel.alignFrame); @@ -356,7 +433,7 @@ public abstract class StructureViewerBase extends GStructureViewer // NO_OPTION - offer the next viewer if any } } - + /* * nothing offered and selected */ @@ -379,9 +456,9 @@ public abstract class StructureViewerBase extends GStructureViewer /* * create the mappings */ - apanel.getStructureSelectionManager().setMapping(seq, chains, pdbFilename, - AppletFormatAdapter.FILE); - + apanel.getStructureSelectionManager().setMapping(seq, chains, + pdbFilename, DataSourceType.FILE); + /* * alert the FeatureRenderer to show new (PDB RESNUM) features */ @@ -390,7 +467,7 @@ public abstract class StructureViewerBase extends GStructureViewer apanel.getSeqPanel().seqCanvas.fr.featuresAdded(); apanel.paintAlignment(true); } - + /* * add the sequences to any other viewers (of the same type) for this pdb * file @@ -398,7 +475,7 @@ public abstract class StructureViewerBase extends GStructureViewer // JBPNOTE: this looks like a binding routine, rather than a gui routine for (StructureViewerBase viewer : getViewersFor(null)) { - AAStructureBindingModel bindingModel = viewer.getBindingModel(); + AAStructureBindingModel bindingModel = viewer.getBinding(); for (int pe = 0; pe < bindingModel.getPdbCount(); pe++) { if (bindingModel.getPdbEntry(pe).getFile().equals(pdbFilename)) @@ -411,7 +488,8 @@ public abstract class StructureViewerBase extends GStructureViewer */ viewer.useAlignmentPanelForColourbyseq(apanel); viewer.buildActionMenu(); - apanel.getStructureSelectionManager().sequenceColoursChanged(apanel); + apanel.getStructureSelectionManager().sequenceColoursChanged( + apanel); break; } } @@ -434,23 +512,25 @@ public abstract class StructureViewerBase extends GStructureViewer boolean finished = false; String alreadyMapped = apanel.getStructureSelectionManager() .alreadyMappedToFile(pdbId); - + if (alreadyMapped != null) { /* * the PDB file is already loaded */ - int option = JOptionPane.showInternalConfirmDialog(Desktop.desktop, - MessageManager.formatMessage( - "label.pdb_entry_is_already_displayed", new Object[] - { pdbId }), MessageManager.formatMessage( - "label.map_sequences_to_visible_window", new Object[] - { pdbId }), JOptionPane.YES_NO_CANCEL_OPTION); - if (option == JOptionPane.CANCEL_OPTION) + int option = JvOptionPane.showInternalConfirmDialog(Desktop.desktop, + MessageManager.formatMessage( + "label.pdb_entry_is_already_displayed", + new Object[] { pdbId }), MessageManager + .formatMessage( + "label.map_sequences_to_visible_window", + new Object[] { pdbId }), + JvOptionPane.YES_NO_CANCEL_OPTION); + if (option == JvOptionPane.CANCEL_OPTION) { finished = true; } - else if (option == JOptionPane.YES_OPTION) + else if (option == JvOptionPane.YES_OPTION) { addSequenceMappingsToStructure(seq, chains, apanel, alreadyMapped); finished = true; @@ -458,4 +538,440 @@ public abstract class StructureViewerBase extends GStructureViewer } return finished; } + + void setChainMenuItems(List chainNames) + { + chainMenu.removeAll(); + if (chainNames == null || chainNames.isEmpty()) + { + return; + } + JMenuItem menuItem = new JMenuItem( + MessageManager.getString("label.all")); + menuItem.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent evt) + { + allChainsSelected = true; + for (int i = 0; i < chainMenu.getItemCount(); i++) + { + if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem) + { + ((JCheckBoxMenuItem) chainMenu.getItem(i)).setSelected(true); + } + } + showSelectedChains(); + allChainsSelected = false; + } + }); + + chainMenu.add(menuItem); + + for (String chain : chainNames) + { + menuItem = new JCheckBoxMenuItem(chain, true); + menuItem.addItemListener(new ItemListener() + { + @Override + public void itemStateChanged(ItemEvent evt) + { + if (!allChainsSelected) + { + showSelectedChains(); + } + } + }); + + chainMenu.add(menuItem); + } + } + + abstract void showSelectedChains(); + + /** + * Action on selecting one of Jalview's registered colour schemes + */ + @Override + public void changeColour_actionPerformed(String colourSchemeName) + { + AlignmentI al = getAlignmentPanel().av.getAlignment(); + ColourSchemeI cs = ColourSchemes.getInstance().getColourScheme( + colourSchemeName, al, null); + getBinding().setJalviewColourScheme(cs); + } + + /** + * Builds the colour menu + */ + protected void buildColourMenu() + { + colourMenu.removeAll(); + AlignmentI al = getAlignmentPanel().av.getAlignment(); + + /* + * add colour by sequence, by chain, by charge and cysteine + */ + colourMenu.add(seqColour); + colourMenu.add(chainColour); + colourMenu.add(chargeColour); + chargeColour.setEnabled(!al.isNucleotide()); + + /* + * add all 'simple' (per-residue) colour schemes registered to Jalview + */ + ButtonGroup itemGroup = ColourMenuHelper.addMenuItems(colourMenu, this, + al, true); + + /* + * add 'colour by viewer' (menu item text is set in subclasses) + */ + viewerColour.setSelected(false); + viewerColour.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent actionEvent) + { + viewerColour_actionPerformed(actionEvent); + } + }); + colourMenu.add(viewerColour); + + /* + * add 'set background colour' + */ + JMenuItem backGround = new JMenuItem(); + backGround + .setText(MessageManager.getString("action.background_colour")); + backGround.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent actionEvent) + { + background_actionPerformed(actionEvent); + } + }); + colourMenu.add(backGround); + + /* + * add colour buttons to a group so their selection is + * mutually exclusive (background colour is a separate option) + */ + itemGroup.add(seqColour); + itemGroup.add(chainColour); + itemGroup.add(chargeColour); + itemGroup.add(viewerColour); + } + + /** + * Construct menu items + */ + protected void initMenus() + { + AAStructureBindingModel binding = getBinding(); + + seqColour = new JRadioButtonMenuItem(); + seqColour.setText(MessageManager.getString("action.by_sequence")); + seqColour.setName(ViewerColour.BySequence.name()); + seqColour.setSelected(binding.isColourBySequence()); + seqColour.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent actionEvent) + { + seqColour_actionPerformed(actionEvent); + } + }); + + chainColour = new JRadioButtonMenuItem(); + chainColour.setText(MessageManager.getString("action.by_chain")); + chainColour.setName(ViewerColour.ByChain.name()); + chainColour.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent actionEvent) + { + chainColour_actionPerformed(actionEvent); + } + }); + + chargeColour = new JRadioButtonMenuItem(); + chargeColour.setText(MessageManager.getString("label.charge_cysteine")); + chargeColour.setName(ViewerColour.ChargeCysteine.name()); + chargeColour.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent actionEvent) + { + chargeColour_actionPerformed(actionEvent); + } + }); + + viewerColour = new JRadioButtonMenuItem(); + // text is set in overrides of this method + viewerColour.setName(ViewerColour.ByViewer.name()); + viewerColour.setSelected(!binding.isColourBySequence()); + + if (_colourwith == null) + { + _colourwith = new Vector(); + } + if (_alignwith == null) + { + _alignwith = new Vector(); + } + + ViewSelectionMenu seqColourBy = new ViewSelectionMenu( + MessageManager.getString("label.colour_by"), this, _colourwith, + new ItemListener() + { + @Override + public void itemStateChanged(ItemEvent e) + { + if (!seqColour.isSelected()) + { + seqColour.doClick(); + } + else + { + // update the Chimera display now. + seqColour_actionPerformed(null); + } + } + }); + viewMenu.add(seqColourBy); + + final ItemListener handler = new ItemListener() + { + @Override + public void itemStateChanged(ItemEvent e) + { + alignStructs.setEnabled(!_alignwith.isEmpty()); + alignStructs.setToolTipText(MessageManager.formatMessage( + "label.align_structures_using_linked_alignment_views", + new String[] { String.valueOf(_alignwith.size()) })); + } + }; + viewSelectionMenu = new ViewSelectionMenu( + MessageManager.getString("label.superpose_with"), this, + _alignwith, handler); + handler.itemStateChanged(null); + viewerActionMenu.add(viewSelectionMenu); + viewerActionMenu.addMenuListener(new MenuListener() + { + @Override + public void menuSelected(MenuEvent e) + { + handler.itemStateChanged(null); + } + + @Override + public void menuDeselected(MenuEvent e) + { + } + + @Override + public void menuCanceled(MenuEvent e) + { + } + }); + + buildColourMenu(); + } + + @Override + public void setJalviewColourScheme(ColourSchemeI cs) { + getBinding().setJalviewColourScheme(cs); + } + @Override + protected void alignStructs_actionPerformed(ActionEvent actionEvent) + { + alignStructs_withAllAlignPanels(); + } + protected void alignStructs_withAllAlignPanels() + { + if (getAlignmentPanel() == null) + { + return; + } + + if (_alignwith.size() == 0) + { + _alignwith.add(getAlignmentPanel()); + } + + try + { + AlignmentI[] als = new Alignment[_alignwith.size()]; + ColumnSelection[] alc = new ColumnSelection[_alignwith.size()]; + int[] alm = new int[_alignwith.size()]; + int a = 0; + + for (AlignmentPanel ap : _alignwith) + { + als[a] = ap.av.getAlignment(); + alm[a] = -1; + alc[a++] = ap.av.getColumnSelection(); + } + getBinding().superposeStructures(als, alm, alc); + } catch (Exception e) + { + StringBuffer sp = new StringBuffer(); + for (AlignmentPanel ap : _alignwith) + { + sp.append("'" + ap.alignFrame.getTitle() + "' "); + } + Cache.log.info("Couldn't align structures with the " + sp.toString() + + "associated alignment panels.", e); + } + } + @Override + public void background_actionPerformed(ActionEvent actionEvent) + { + Color col = JColorChooser.showDialog(this, + MessageManager.getString("label.select_background_colour"), + null); + if (col != null) + { + getBinding().setBackgroundColour(col); + } + } + @Override + public void viewerColour_actionPerformed(ActionEvent actionEvent) + { + if (viewerColour.isSelected()) + { + // disable automatic sequence colouring. + getBinding().setColourBySequence(false); + } + } + @Override + public void chainColour_actionPerformed(ActionEvent actionEvent) + { + chainColour.setSelected(true); + getBinding().colourByChain(); + } + @Override + public void chargeColour_actionPerformed(ActionEvent actionEvent) + { + chargeColour.setSelected(true); + getBinding().colourByCharge(); + } + @Override + public void seqColour_actionPerformed(ActionEvent actionEvent) + { + AAStructureBindingModel binding = getBinding(); + binding.setColourBySequence(seqColour.isSelected()); + if (_colourwith == null) + { + _colourwith = new Vector(); + } + if (binding.isColourBySequence()) + { + if (!binding.isLoadingFromArchive()) + { + if (_colourwith.size() == 0 && getAlignmentPanel() != null) + { + // Make the currently displayed alignment panel the associated view + _colourwith.add(getAlignmentPanel().alignFrame.alignPanel); + } + } + // Set the colour using the current view for the associated alignframe + for (AlignmentPanel ap : _colourwith) + { + binding.colourBySequence(ap); + } + } + } + @Override + public void pdbFile_actionPerformed(ActionEvent actionEvent) + { + JalviewFileChooser chooser = new JalviewFileChooser( + Cache.getProperty("LAST_DIRECTORY")); + + chooser.setFileView(new JalviewFileView()); + chooser.setDialogTitle(MessageManager.getString("label.save_pdb_file")); + chooser.setToolTipText(MessageManager.getString("action.save")); + + int value = chooser.showSaveDialog(this); + + if (value == JalviewFileChooser.APPROVE_OPTION) + { + BufferedReader in = null; + try + { + // TODO: cope with multiple PDB files in view + in = new BufferedReader( + new FileReader(getBinding().getPdbFile()[0])); + File outFile = chooser.getSelectedFile(); + + PrintWriter out = new PrintWriter(new FileOutputStream(outFile)); + String data; + while ((data = in.readLine()) != null) + { + if (!(data.indexOf("
") > -1 || data.indexOf("
") > -1)) + { + out.println(data); + } + } + out.close(); + } catch (Exception ex) + { + ex.printStackTrace(); + } finally + { + if (in != null) + { + try + { + in.close(); + } catch (IOException e) + { + // ignore + } + } + } + } + } + @Override + public void viewMapping_actionPerformed(ActionEvent actionEvent) + { + CutAndPasteTransfer cap = new CutAndPasteTransfer(); + try + { + cap.appendText(getBinding().printMappings()); + } catch (OutOfMemoryError e) + { + new OOMWarning( + "composing sequence-structure alignments for display in text box.", + e); + cap.dispose(); + return; + } + Desktop.addInternalFrame(cap, + MessageManager.getString("label.pdb_sequence_mapping"), 550, + 600); + } + + protected abstract String getViewerName(); + public void updateTitleAndMenus() + { + AAStructureBindingModel binding = getBinding(); + if (binding.hasFileLoadingError()) + { + repaint(); + return; + } + setChainMenuItems(binding.getChainNames()); + + this.setTitle(binding.getViewerTitle(getViewerName(), true)); + if (binding.getPdbFile().length > 1 && binding.getSequence().length > 1) + { + viewerActionMenu.setVisible(true); + } + if (!binding.isLoadingFromArchive()) + { + seqColour_actionPerformed(null); + } + } }