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.datamodel.PDBEntry;
24 import jalview.datamodel.SequenceI;
25 import jalview.gui.StructureViewer.ViewerType;
26 import jalview.gui.ViewSelectionMenu.ViewSetProvider;
27 import jalview.io.DataSourceType;
28 import jalview.jbgui.GStructureViewer;
29 import jalview.structures.models.AAStructureBindingModel;
30 import jalview.util.MessageManager;
32 import java.awt.Component;
33 import java.awt.event.ActionEvent;
34 import java.awt.event.ActionListener;
35 import java.awt.event.ItemEvent;
36 import java.awt.event.ItemListener;
37 import java.util.ArrayList;
38 import java.util.Collections;
39 import java.util.List;
40 import java.util.Vector;
42 import javax.swing.JCheckBoxMenuItem;
43 import javax.swing.JMenu;
44 import javax.swing.JMenuItem;
47 * Base class with common functionality for JMol, Chimera or other structure
53 public abstract class StructureViewerBase extends GStructureViewer
54 implements Runnable, ViewSetProvider
57 * prefix for attributes on structure which are derived from Jalview features
59 public static final String NAMESPACE_PREFIX = "jv_";
62 * list of sequenceSet ids associated with the view
64 protected List<String> _aps = new ArrayList<String>();
67 * list of alignment panels to use for superposition
69 protected Vector<AlignmentPanel> _alignwith = new Vector<AlignmentPanel>();
72 * list of alignment panels that are used for colouring structures by aligned
75 protected Vector<AlignmentPanel> _colourwith = new Vector<AlignmentPanel>();
77 private String viewId = null;
79 private AlignmentPanel ap;
81 protected boolean alignAddedStructures = false;
83 protected boolean _started = false;
85 protected boolean addingStructures = false;
87 protected Thread worker = null;
89 protected boolean allChainsSelected = false;
94 * @return true if this Jmol instance is linked with the given alignPanel
96 public boolean isLinkedWith(AlignmentPanel ap2)
98 return _aps.contains(ap2.av.getSequenceSetId());
101 public boolean isUsedforaligment(AlignmentPanel ap2)
104 return (_alignwith != null) && _alignwith.contains(ap2);
107 public boolean isUsedforcolourby(AlignmentPanel ap2)
109 return (_colourwith != null) && _colourwith.contains(ap2);
114 * @return TRUE if the view is NOT being coloured by the alignment colours.
116 public boolean isColouredByViewer()
118 return !getBinding().isColourBySequence();
121 public String getViewId()
125 viewId = System.currentTimeMillis() + "." + this.hashCode();
130 protected void setViewId(String viewId)
132 this.viewId = viewId;
135 public abstract String getStateInfo();
137 protected void buildActionMenu()
139 if (_alignwith == null)
141 _alignwith = new Vector<AlignmentPanel>();
143 if (_alignwith.size() == 0 && ap != null)
148 for (Component c : viewerActionMenu.getMenuComponents())
150 if (c != alignStructs)
152 viewerActionMenu.remove((JMenuItem) c);
157 public AlignmentPanel getAlignmentPanel()
162 protected void setAlignmentPanel(AlignmentPanel alp)
168 public AlignmentPanel[] getAllAlignmentPanels()
170 AlignmentPanel[] t, list = new AlignmentPanel[0];
171 for (String setid : _aps)
173 AlignmentPanel[] panels = PaintRefresher.getAssociatedPanels(setid);
176 t = new AlignmentPanel[list.length + panels.length];
177 System.arraycopy(list, 0, t, 0, list.length);
178 System.arraycopy(panels, 0, t, list.length, panels.length);
187 * set the primary alignmentPanel reference and add another alignPanel to the
188 * list of ones to use for colouring and aligning
192 public void addAlignmentPanel(AlignmentPanel nap)
194 if (getAlignmentPanel() == null)
196 setAlignmentPanel(nap);
198 if (!_aps.contains(nap.av.getSequenceSetId()))
200 _aps.add(nap.av.getSequenceSetId());
205 * remove any references held to the given alignment panel
209 public void removeAlignmentPanel(AlignmentPanel nap)
213 _alignwith.remove(nap);
214 _colourwith.remove(nap);
215 if (getAlignmentPanel() == nap)
217 setAlignmentPanel(null);
218 for (AlignmentPanel aps : getAllAlignmentPanels())
222 setAlignmentPanel(aps);
227 } catch (Exception ex)
230 if (getAlignmentPanel() != null)
236 public void useAlignmentPanelForSuperposition(AlignmentPanel nap)
238 addAlignmentPanel(nap);
239 if (!_alignwith.contains(nap))
245 public void excludeAlignmentPanelForSuperposition(AlignmentPanel nap)
247 if (_alignwith.contains(nap))
249 _alignwith.remove(nap);
253 public void useAlignmentPanelForColourbyseq(AlignmentPanel nap,
254 boolean enableColourBySeq)
256 useAlignmentPanelForColourbyseq(nap);
257 getBinding().setColourBySequence(enableColourBySeq);
258 seqColour.setSelected(enableColourBySeq);
259 viewerColour.setSelected(!enableColourBySeq);
262 public void useAlignmentPanelForColourbyseq(AlignmentPanel nap)
264 addAlignmentPanel(nap);
265 if (!_colourwith.contains(nap))
267 _colourwith.add(nap);
271 public void excludeAlignmentPanelForColourbyseq(AlignmentPanel nap)
273 if (_colourwith.contains(nap))
275 _colourwith.remove(nap);
279 public abstract ViewerType getViewerType();
281 protected abstract AAStructureBindingModel getBindingModel();
284 * add a new structure (with associated sequences and chains) to this viewer,
285 * retrieving it if necessary first.
291 * if true, new structure(s) will be aligned using associated
295 protected void addStructure(final PDBEntry pdbentry,
296 final SequenceI[] seqs, final String[] chains,
297 final boolean align, final IProgressIndicator alignFrame)
299 if (pdbentry.getFile() == null)
301 if (worker != null && worker.isAlive())
303 // a retrieval is in progress, wait around and add ourselves to the
305 new Thread(new Runnable()
310 while (worker != null && worker.isAlive() && _started)
314 Thread.sleep(100 + ((int) Math.random() * 100));
316 } catch (Exception e)
320 // and call ourselves again.
321 addStructure(pdbentry, seqs, chains, align, alignFrame);
327 // otherwise, start adding the structure.
328 getBindingModel().addSequenceAndChain(new PDBEntry[] { pdbentry },
329 new SequenceI[][] { seqs }, new String[][] { chains });
330 addingStructures = true;
332 alignAddedStructures = align;
333 worker = new Thread(this);
339 * Presents a dialog with the option to add an align a structure to an
340 * existing structure view
344 * @return YES, NO or CANCEL JvOptionPane code
346 protected int chooseAlignStructureToViewer(String pdbId,
347 StructureViewerBase view)
349 int option = JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
350 MessageManager.formatMessage("label.add_pdbentry_to_view",
351 new Object[] { pdbId, view.getTitle() }),
353 .getString("label.align_to_existing_structure_view"),
354 JvOptionPane.YES_NO_CANCEL_OPTION);
358 protected abstract boolean hasPdbId(String pdbId);
360 protected abstract List<StructureViewerBase> getViewersFor(
364 * Check for any existing views involving this alignment and give user the
365 * option to add and align this molecule to one of them
372 * @return true if user adds to a view, or cancels entirely, else false
374 protected boolean addToExistingViewer(PDBEntry pdbentry, SequenceI[] seq,
375 String[] chains, final AlignmentPanel apanel, String pdbId)
377 for (StructureViewerBase view : getViewersFor(apanel))
379 // TODO: highlight the view somehow
381 * JAL-1742 exclude view with this structure already mapped (don't offer
382 * to align chain B to chain A of the same structure)
384 if (view.hasPdbId(pdbId))
388 int option = chooseAlignStructureToViewer(pdbId, view);
389 if (option == JvOptionPane.CANCEL_OPTION)
393 else if (option == JvOptionPane.YES_OPTION)
395 view.useAlignmentPanelForSuperposition(apanel);
396 view.addStructure(pdbentry, seq, chains, true, apanel.alignFrame);
401 // NO_OPTION - offer the next viewer if any
406 * nothing offered and selected
412 * Adds mappings for the given sequences to an already opened PDB structure,
413 * and updates any viewers that have the PDB file
420 protected void addSequenceMappingsToStructure(SequenceI[] seq,
421 String[] chains, final AlignmentPanel apanel, String pdbFilename)
423 // TODO : Fix multiple seq to one chain issue here.
425 * create the mappings
427 apanel.getStructureSelectionManager().setMapping(seq, chains,
428 pdbFilename, DataSourceType.FILE);
431 * alert the FeatureRenderer to show new (PDB RESNUM) features
433 if (apanel.getSeqPanel().seqCanvas.fr != null)
435 apanel.getSeqPanel().seqCanvas.fr.featuresAdded();
436 apanel.paintAlignment(true);
440 * add the sequences to any other viewers (of the same type) for this pdb
443 // JBPNOTE: this looks like a binding routine, rather than a gui routine
444 for (StructureViewerBase viewer : getViewersFor(null))
446 AAStructureBindingModel bindingModel = viewer.getBindingModel();
447 for (int pe = 0; pe < bindingModel.getPdbCount(); pe++)
449 if (bindingModel.getPdbEntry(pe).getFile().equals(pdbFilename))
451 bindingModel.addSequence(pe, seq);
452 viewer.addAlignmentPanel(apanel);
454 * add it to the set of alignments used for colouring structure by
457 viewer.useAlignmentPanelForColourbyseq(apanel);
458 viewer.buildActionMenu();
459 apanel.getStructureSelectionManager().sequenceColoursChanged(
468 * Check if the PDB file is already loaded, if so offer to add it to the
475 * @return true if the user chooses to add to a viewer, or to cancel entirely
477 protected boolean addAlreadyLoadedFile(SequenceI[] seq, String[] chains,
478 final AlignmentPanel apanel, String pdbId)
480 boolean finished = false;
481 String alreadyMapped = apanel.getStructureSelectionManager()
482 .alreadyMappedToFile(pdbId);
484 if (alreadyMapped != null)
487 * the PDB file is already loaded
489 int option = JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
490 MessageManager.formatMessage(
491 "label.pdb_entry_is_already_displayed",
492 new Object[] { pdbId }), MessageManager
494 "label.map_sequences_to_visible_window",
495 new Object[] { pdbId }),
496 JvOptionPane.YES_NO_CANCEL_OPTION);
497 if (option == JvOptionPane.CANCEL_OPTION)
501 else if (option == JvOptionPane.YES_OPTION)
503 addSequenceMappingsToStructure(seq, chains, apanel, alreadyMapped);
510 void setChainMenuItems(List<String> chainNames)
512 chainMenu.removeAll();
513 if (chainNames == null || chainNames.isEmpty())
517 JMenuItem menuItem = new JMenuItem(
518 MessageManager.getString("label.all"));
519 menuItem.addActionListener(new ActionListener()
522 public void actionPerformed(ActionEvent evt)
524 allChainsSelected = true;
525 for (int i = 0; i < chainMenu.getItemCount(); i++)
527 if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem)
529 ((JCheckBoxMenuItem) chainMenu.getItem(i)).setSelected(true);
532 showSelectedChains();
533 allChainsSelected = false;
537 chainMenu.add(menuItem);
539 for (String chain : chainNames)
541 menuItem = new JCheckBoxMenuItem(chain, true);
542 menuItem.addItemListener(new ItemListener()
545 public void itemStateChanged(ItemEvent evt)
547 if (!allChainsSelected)
549 showSelectedChains();
554 chainMenu.add(menuItem);
558 abstract void showSelectedChains();
561 * Send a command to the structure viewer to create residue attributes for
564 abstract protected void sendFeaturesToViewer();
567 * Query the structure viewer for its residue attribute names and add them as
568 * sub-items of the attributes menu. Names of the form "jv_*" are ignored as
569 * these originated from Jalview so no need to copy them back.
571 * @param attributesMenu
573 protected void buildAttributesMenu(JMenu attributesMenu)
575 List<String> atts = getResidueAttributeNames();
580 attributesMenu.removeAll();
581 Collections.sort(atts);
582 for (final String att : atts)
585 * ignore 'jv_*' attributes, as these are Jalview features that have
586 * been transferred to residue attributes in Chimera!
588 if (!att.startsWith(StructureViewerBase.NAMESPACE_PREFIX))
590 JMenuItem menuItem = new JMenuItem(att);
591 menuItem.addActionListener(new ActionListener()
594 public void actionPerformed(ActionEvent e)
596 getResidueAttributes(att);
599 attributesMenu.add(menuItem);
605 * Queries the structure viewer for residues with the given attribute, and
606 * creates sequence features in Jalview for the corresponding mapped positions
610 protected abstract void getResidueAttributes(String attName);
613 * Returns a list of residue attributes in the structure viewer, excluding any
618 protected abstract List<String> getResidueAttributeNames();