/* * Copyright (c) 2014. Matthew Campbell <matthew.campbell@mq.edu.au>, David R. Damerell <david@nixbioinf.org>. * * This file is part of GlycanBuilder Vaadin Release and its affliated projects EUROCarbDB, UniCarb-DB and UniCarbKB. * * This program is free software 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. * * GlycanBuilder Vaadin Release 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 (LICENSE.txt) for more details. * * You should have received a copy of the GNU General Public License * along with GlycanBuilder Vaadin Release. If not, see <http ://www.gnu.org/licenses/>. */ package ac.uk.icl.dell.vaadin.glycanbuilder; import static org.eurocarbdb.application.glycanbuilder.Geometry.center; import static org.eurocarbdb.application.glycanbuilder.Geometry.distance; import java.awt.Point; import java.awt.Rectangle; import java.awt.datatransfer.Transferable; import java.io.InputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.Vector; import org.eurocarbdb.application.glycanbuilder.BBoxManager; import org.eurocarbdb.application.glycanbuilder.BaseDocument.DocumentChangeEvent; import org.eurocarbdb.application.glycanbuilder.BaseDocument.DocumentChangeListener; import org.eurocarbdb.application.glycanbuilder.BuilderWorkspace; import org.eurocarbdb.application.glycanbuilder.CoreDictionary; import org.eurocarbdb.application.glycanbuilder.Glycan; import org.eurocarbdb.application.glycanbuilder.GlycanDocument; import org.eurocarbdb.application.glycanbuilder.GlycanRenderer; import org.eurocarbdb.application.glycanbuilder.GlycanRendererMode; import org.eurocarbdb.application.glycanbuilder.GlycanSelection; import org.eurocarbdb.application.glycanbuilder.GraphicOptions; import org.eurocarbdb.application.glycanbuilder.Linkage; import org.eurocarbdb.application.glycanbuilder.LogUtils; import org.eurocarbdb.application.glycanbuilder.Paintable; import org.eurocarbdb.application.glycanbuilder.PositionManager; import org.eurocarbdb.application.glycanbuilder.ResAngle; import org.eurocarbdb.application.glycanbuilder.Residue; import org.eurocarbdb.application.glycanbuilder.ResidueDictionary; import org.eurocarbdb.application.glycanbuilder.ResiduePlacement; import org.eurocarbdb.application.glycanbuilder.ResidueSelectorDialog; import org.eurocarbdb.application.glycanbuilder.ResidueType; import org.eurocarbdb.application.glycanbuilder.SVGUtils; import org.eurocarbdb.application.glycanbuilder.TerminalDictionary; import org.eurocarbdb.application.glycanbuilder.TextUtils; public class GlycanCanvas implements DocumentChangeListener, Serializable{ private static final long serialVersionUID=3243669659305552904L; protected HashMap<Rectangle,Residue> boundingBoxes=new HashMap<Rectangle,Residue>(); protected BBoxManager theBBoxManager; protected PositionManager posManager; protected HashSet<Linkage> selectedLinkages; protected HashSet<Residue> selectedResidues; //protected GlycanRenderer theGlycanRenderer; protected org.eurocarbdb.application.glycanbuilder.BuilderWorkspace theWorkspace; protected org.eurocarbdb.application.glycanbuilder.GlycanDocument theDoc; protected HashSet<String> residueTypeHistory=new HashSet<String>(); protected Vector<String> residueTypeHistoryList=new Vector<String>(); protected HashSet<ResidueHistoryListener> residueHistoryListeners=new HashSet<ResidueHistoryListener>(); @SuppressWarnings("unused") private Collection<Glycan> sel; private Linkage currentLinkage; private Residue currentResidue; private boolean pauseSelectionUpdates=false; public org.eurocarbdb.application.glycanbuilder.GlycanDocument getTheDoc() { return theDoc; } public void setDocument(GlycanDocument doc){ resetSelection(); theDoc.removeDocumentChangeListener(this); theDoc=doc; theDoc.addDocumentChangeListener(this); documentUpdated(); } protected Paintable thePaintable; protected int height; protected int width; @SuppressWarnings("unused") private boolean residueSelected=false; private boolean documentChangedEventFiring=true; public boolean isDocumentChangedEventFiring() { return documentChangedEventFiring; } public void setDocumentChangedEventFiring(boolean documentChangedEventFiring) { this.documentChangedEventFiring = documentChangedEventFiring; } public int getHeight() { return height; } public int getWidth(){ return width; } protected HashSet<GlycanCanvasUpdateListener> glycanCanvasUpdateListeners; public interface GlycanCanvasUpdateListener { public void glycanCanvasUpdated(); } public void fireGlycanCanvasUpdated(){ if(documentChangedEventFiring){ for(GlycanCanvasUpdateListener glycanCanvasListener:glycanCanvasUpdateListeners){ glycanCanvasListener.glycanCanvasUpdated(); } } } public void addGlycanCanvasUpdateListener(GlycanCanvasUpdateListener glycanCanvasUpdateListener){ glycanCanvasUpdateListeners.add(glycanCanvasUpdateListener); } public GlycanCanvas(GlycanRenderer glycanRenderer,Paintable paintable) { thePaintable=paintable; theWorkspace=new org.eurocarbdb.application.glycanbuilder.BuilderWorkspace(glycanRenderer); //theGlycanRenderer=theWorkspace.getGlycanRenderer(); posManager = new PositionManager(); theBBoxManager = new BBoxManager(); selectedResidues=new HashSet<Residue>(); selectedLinkages=new HashSet<Linkage>(); theDoc=theWorkspace.getStructures(); theDoc.addDocumentChangeListener(this); glycanCanvasUpdateListeners=new HashSet<GlycanCanvasUpdateListener>(); } public void setNotation(String notation) { theWorkspace.setNotation(notation); //theGlycanRenderer=theWorkspace.getGlycanRenderer(); documentUpdated(); fireNotationChanged(); } public void addSequenceByName(String name){ try { theDoc.addStructure(CoreDictionary.newCore(name)); } catch (Exception e) { e.printStackTrace(); } } public void addResidueByNameToSelected(String name){ GlycanRenderer theGlycanRenderer=theWorkspace.getGlycanRenderer(); if(theGlycanRenderer.getRenderMode()==GlycanRendererMode.TOOLBAR || selectedResidues.size()==0){ try{ Residue toAdd = ResidueDictionary.newResidue(name); theDoc.addResidue(null, getLinkedResidues(),toAdd); if(theGlycanRenderer.getRenderMode()!=GlycanRendererMode.TOOLBAR){ setSelection(toAdd); } } catch (Exception e) { e.printStackTrace(); } }else{ for(Residue selectedResidue:selectedResidues){ try { Residue toAdd = ResidueDictionary.newResidue(name); if (theDoc.addResidue(selectedResidue, getLinkedResidues(),toAdd) != null) { setSelection(toAdd); } } catch (Exception e) { e.printStackTrace(); } } } addResidueToHistory(name); } public void changeSelectedToResidueByName(String name){ boolean changed=false; for(Residue selectedResidue:selectedResidues){ try{ ResidueType newType = ResidueDictionary.getResidueType(name); if (theDoc.changeResidueType(selectedResidue, getLinkedResidues(), newType)) { //theWorkspace.getResidueHistory().add(current); changed=true; } }catch(Exception e){ e.printStackTrace(); } } if(changed){ addResidueToHistory(name); } } public void insertResidueByNameBeforeSelected(String name){ boolean inserted=false; for(Residue selectedResidue:selectedResidues){ System.err.println("AI: "+selectedResidue.getTypeName()); try{ Residue toInsert = ResidueDictionary.newResidue(name); if (theDoc.insertResidueBefore(selectedResidue, getLinkedResidues(),toInsert) != null) { inserted=true; } }catch(Exception e){ e.printStackTrace(); } } if(inserted){ addResidueToHistory(name); } } public String[] toStrings(char[] pos) { String[] ret = new String[pos.length]; for (int i = 0; i < pos.length; i++) ret[i] = "" + pos[i]; return ret; } public List<String> createPositions(Residue parent) { List<String> ret = new ArrayList<String>(); // collect available positions char[] par_pos = null; if (parent == null || parent.getType().getLinkagePositions().length == 0) par_pos = new char[] { '1', '2', '3', '4', '5', '6', '7', '8', '9', 'N' }; else par_pos = parent.getType().getLinkagePositions(); // add elements ret.add("?"); for (int i = 0; i < par_pos.length; i++) ret.add("" + par_pos[i]); return ret; } public void resetSelection(){ selectedResidues.clear(); selectedLinkages.clear(); currentResidue = null; currentLinkage = null; fireUpdatedSelection(); } public void setSelection(Linkage link) { pauseSelectionUpdates=true; resetSelection(); if(link!=null){ selectedLinkages.add(link); currentLinkage = link; } pauseSelectionUpdates=false; fireUpdatedSelection(); } public void setSelection(Residue node) { pauseSelectionUpdates=true; resetSelection(); if(node!=null){ selectedResidues.add(node); selectedResidues.addAll(theBBoxManager.getLinkedResidues(node)); currentResidue = node; } pauseSelectionUpdates=false; fireUpdatedSelection(); } public void documentUpdated(){ documentUpdated(false); } public void documentUpdated(boolean selectionRedraw){ thePaintable.clear(); posManager = new PositionManager(); theBBoxManager = new BBoxManager(); if(!theDoc.isEmpty()){ theWorkspace.getGlycanRenderer().computeBoundingBoxes(theDoc.getStructures(), theWorkspace.getGraphicOptions().SHOW_MASSES_CANVAS,theWorkspace.getGraphicOptions().SHOW_REDEND_CANVAS, posManager, theBBoxManager); /*TODO-PP if(thePaintable instanceof CanvasPaintable){ if(selectionRedraw){ //TODO: re-add scrollTo method. ((CanvasPaintable)thePaintable).canvas.setScroll(-1,-1); }else{ if(currentResidue!=null){ //Rectangle rec=theBBoxManager.getBBox(glycan, theWorkspace.getGraphicOptions().SHOW_REDEND_CANVAS); Rectangle rec=theBBoxManager.getBorder(currentResidue); if(rec!=null){ System.out.println("Rec: "+rec); ((CanvasPaintable)thePaintable).canvas.setScroll(rec.y,rec.x); } }else{ Rectangle rec=theBBoxManager.getBBox(theDoc.getLastStructure(),theWorkspace.getGraphicOptions().SHOW_REDEND_CANVAS); if(rec!=null){ System.out.println("Rec: "+rec); ((CanvasPaintable)thePaintable).canvas.setScroll(rec.y,rec.x); } } } }*/ for(Glycan glycan:theDoc.getStructures()){ theWorkspace.getGlycanRenderer().paint(thePaintable,glycan, selectedResidues, selectedLinkages, theWorkspace.getGraphicOptions().SHOW_MASSES_CANVAS,theWorkspace.getGraphicOptions().SHOW_REDEND_CANVAS, posManager, theBBoxManager); } } updateCanvasHeight(); fireGlycanCanvasUpdated(); } @Override public void documentInit(DocumentChangeEvent e) { } @Override public void documentChanged(DocumentChangeEvent e) { if(documentChangedEventFiring){ documentUpdated(); } } /** * Return the residue with the focus */ public Residue getCurrentResidue() { return currentResidue; } /** * Return all the residues that are shown at the same position of the * residue with the focus */ public Vector<Residue> getLinkedResidues() { return theBBoxManager.getLinkedResidues(getCurrentResidue()); } /** * Delete the selected residues/structures and copy them to the clipboard */ // public void cut() { // copy(); // delete(); // } Transferable clipBoard; /** * Delete the selected residues/structures and copy them to the clipboard */ public void cut() { copy(); delete(); } /** * Copy the selected residues/structures to the clipboard */ public void copy() { Collection<Glycan> sel = theDoc.extractView(selectedResidues); clipBoard=new GlycanSelection(theWorkspace.getGlycanRenderer(), sel); } /** * Copy all selected structures to the clipboard */ public void copySelectedStructures() { // ClipUtils.setContents(new GlycanSelection(getTheGlycanRenderer(), // getSelectedStructures())); } /** * Copy all structures to the clipboard */ public void copyAllStructures() { // ClipUtils.setContents(new GlycanSelection(getTheGlycanRenderer(), theDoc // .getStructures())); } /** * Paste the content of the clipboard into the editor */ public void paste() { try { Transferable t = clipBoard; if (t != null && t.isDataFlavorSupported(GlycanSelection.glycoFlavor)) { String content = TextUtils.consume((InputStream) t.getTransferData(GlycanSelection.glycoFlavor)); if(selectedResidues.size()==0){ for(Glycan glycan:theDoc.parseString(content)){ theDoc.addStructure(glycan); } }else{ for(Residue selectedResidue:selectedResidues){ theDoc.addStructures(selectedResidue, theDoc .parseString(content)); } } } } catch (Exception e) { } } /** * Delete the selected residues/structures */ public void delete() { @SuppressWarnings("unchecked") HashSet<Residue> shallowClone=(HashSet<Residue>) selectedResidues.clone(); selectedResidues.clear(); theDoc.removeResidues(shallowClone); } /** * Return the linkage displayed at the specified position, or * <code>null</code> if none is there */ public Linkage getLinkageAtPoint(Point p) { for (Glycan g : theDoc.getStructures()) { Linkage ret = getLinkageAtPoint(g.getRoot(), p); if (ret != null){ return ret; } ret = getLinkageAtPoint(g.getBracket(), p); if (ret != null){ return ret; } } System.err.println("No linkage selected!"); return null; } /** * Return the linkage from a residue that is displayed at the specified * position, or <code>null</code> if none is there * * @param r * the parent */ public Linkage getLinkageAtPoint(Residue r, Point p) { if (r == null) return null; Rectangle cur_bbox = theBBoxManager.getCurrent(r); for (Linkage l : r.getChildrenLinkages()) { Rectangle child_bbox = theBBoxManager.getCurrent(l .getChildResidue()); if (cur_bbox != null && child_bbox != null && distance(p, center(cur_bbox), center(child_bbox)) < 4.) return l; Linkage ret = getLinkageAtPoint(l.getChildResidue(), p); if (ret != null) return ret; } return null; } public boolean hasSelected(){ return selectedResidues.size()>0 || selectedLinkages.size()>0 ? true : false; } public boolean hasSelectedResidues(){ return selectedResidues.size()>0 ? true : false; } public void selectIntersectingRectangles(double x, double y, double width,double height,boolean mouseMoved){ pauseSelectionUpdates=true; boolean hadSelected=hasSelected(); List<Residue> selected=new ArrayList<Residue>(); for(Residue residue:theBBoxManager.current_bboxes.keySet()){ Rectangle rec=theBBoxManager.current_bboxes.get(residue); if(mouseMoved){ if(rec.y < y+height && rec.y+rec.height > y && rec.x < x+width && rec.x+rec.width > x){ selected.add(residue); selected.addAll(theBBoxManager.getLinkedResidues(residue)); } }else{ if(x>=rec.x && x<=rec.x+rec.width && y>=rec.y && y<=rec.y+rec.height){ selected.add(residue); selected.addAll(theBBoxManager.getLinkedResidues(residue)); } } } if(selected.size()==1){ setSelection(selected.get(0)); }else{ setSelection(selected); } if(!hasSelected()){ Linkage link = getLinkageAtPoint(new Point((int)x,(int)y)); setSelection(link); } pauseSelectionUpdates=false; if(hadSelected==false && hasSelected()==false){ return; }else{ fireUpdatedSelection(); } } public void setSelection(Collection<Residue> nodes) { resetSelection(); if(nodes != null && !nodes.isEmpty()){ for (Residue node : nodes) { selectedResidues.add(node); selectedResidues.addAll(theBBoxManager.getLinkedResidues(node)); } fireUpdatedSelection(); } } public interface SelectionChangeListener { public void selectionChanged(); } private HashSet<SelectionChangeListener> selectionChangeListeners=new HashSet<SelectionChangeListener>(); public void addSelectionChangeListener(SelectionChangeListener listener){ selectionChangeListeners.add(listener); } public void removeSelectionChangeListener(SelectionChangeListener listener){ selectionChangeListeners.remove(listener); } public void fireUpdatedSelection(){ if(!pauseSelectionUpdates){ documentUpdated(true); for(SelectionChangeListener listener:selectionChangeListeners){ listener.selectionChanged(); } } } public void selectAllResiduesByName(HashSet<String> residueNames){ for(String residueName:residueNames){ selectAllResiduesByName(residueName); } } public void selectAllResiduesByName(String name){ for(Glycan glycan:theDoc.getStructures()){ for(Residue residue:glycan.getAllResidues()){ if(residue.getTypeName().equals(name)){ selectedResidues.add(residue); } } } } private void updateCanvasHeight(){ height=-1; for(Residue residue:theBBoxManager.complete_bboxes.keySet()){ Rectangle rec=theBBoxManager.complete_bboxes.get(residue); if(rec.y+rec.height > height){ height=rec.y+rec.height; } } GlycanRenderer theGlycanRenderer=theWorkspace.getGlycanRenderer(); if(theGlycanRenderer.getRenderMode()!=GlycanRendererMode.TOOLBAR){ height=height+theWorkspace.getGraphicOptions().MARGIN_TOP+theWorkspace.getGraphicOptions().MARGIN_BOTTOM+theWorkspace.getGraphicOptions().MASS_TEXT_SPACE; } if(theGlycanRenderer.getRenderMode()==GlycanRendererMode.TOOLBAR){ width=0; for(Residue residue:theBBoxManager.complete_bboxes.keySet()){ Rectangle rec=theBBoxManager.complete_bboxes.get(residue); width+=rec.width+theGlycanRenderer.getGraphicOptions().STRUCTURES_SPACE; } }else{ width=-1; for(Residue residue:theBBoxManager.complete_bboxes.keySet()){ Rectangle rec=theBBoxManager.complete_bboxes.get(residue); if(rec.x+rec.width > width){ width=rec.x+rec.width; } } } } public interface ResidueHistoryListener{ public void respondToResidueHistoryChanged(Vector<String> residueHistory); } public void addResidueToHistory(String typeName){ if(theWorkspace.getGlycanRenderer().getRenderMode()==GlycanRendererMode.DRAWING && !residueTypeHistory.contains(typeName)){ int maxResidues=5; if(residueTypeHistoryList.size() >maxResidues){ residueTypeHistory.remove(residueTypeHistoryList.remove(0)); } residueTypeHistory.add(typeName); residueTypeHistoryList.add(typeName); fireResidueHistoryChanged(); } } protected void fireResidueHistoryChanged(){ for(ResidueHistoryListener listener:residueHistoryListeners){ listener.respondToResidueHistoryChanged(residueTypeHistoryList); } } public void addResidueHistoryListener(ResidueHistoryListener listener){ residueHistoryListeners.add(listener); } public void removeResidueHistoryListener(ResidueHistoryListener listener){ residueHistoryListeners.remove(listener); } public void clear(){ theDoc.clear(); selectedResidues.clear(); selectedLinkages.clear(); } public String getOrientationIcon(){ if(theWorkspace.getGraphicOptions().ORIENTATION==GraphicOptions.BT){ return "bt.png"; }else if(theWorkspace.getGraphicOptions().ORIENTATION==GraphicOptions.LR){ return "lr.png"; }else if(theWorkspace.getGraphicOptions().ORIENTATION==GraphicOptions.RL){ return "rl.png"; }else{ return "tb.png"; } } /** * Change the orientation of the display */ public void changeOrientation() { theWorkspace.getGraphicOptions().ORIENTATION = (theWorkspace.getGraphicOptions().ORIENTATION + 1) % 4; documentUpdated(); } HashSet<NotationChangedListener> notationChangeListeners=new HashSet<NotationChangedListener>(); protected boolean selectionRender=false; public interface NotationChangedListener { public void notationChanged(String notation); } public void addNotationListener(NotationChangedListener listener){ notationChangeListeners.add(listener); } public void removeNotationListener(NotationChangedListener listener){ notationChangeListeners.remove(listener); } public void fireNotationChanged(){ for(NotationChangedListener listener:notationChangeListeners){ listener.notationChanged(theWorkspace.getGraphicOptions().NOTATION); } } public void selectAll() { for (Iterator<Glycan> i = theDoc.iterator(); i.hasNext();) { Glycan structure = i.next(); selectAll(structure.getRoot()); selectAll(structure.getBracket()); } selectedLinkages.clear(); currentResidue = null; currentLinkage = null; if (theDoc.getFirstStructure() != null){ currentResidue = theDoc.getFirstStructure().getRoot(); } } private void selectAll(Residue node) { if (node == null){ return; } selectedResidues.add(node); for (Iterator<Linkage> i = node.iterator(); i.hasNext();){ selectAll(i.next().getChildResidue()); } } public String getInternalFormat(String longFormat){ for(java.util.Map.Entry<String,String> e : GlycanDocument.getExportFormats().entrySet()){ if(longFormat.equals(e.getValue())){ return e.getKey(); } } return null; } public List<String> getSequenceExportFormats(){ List<String> exportFormats=new ArrayList<String>(); for(java.util.Map.Entry<String,String> e : GlycanDocument.getExportFormats().entrySet()){ exportFormats.add(e.getValue()); } return exportFormats; } public String getImageExportShortFormat(String longFormat){ for(Entry<String, String> e : SVGUtils.getExportFormats().entrySet()){ if(longFormat.equals(e.getValue())){ return e.getKey(); } } return null; } public List<String> getImageExportFormats(){ List<String> exportFormats=new ArrayList<String>(); for(Entry<String, String> e : SVGUtils.getExportFormats().entrySet()){ exportFormats.add(e.getValue()); } return exportFormats; } public List<String> getImportFormats(){ List<String> importFormats=new ArrayList<String>(); for(java.util.Map.Entry<String,String> e : GlycanDocument.getImportFormats().entrySet()){ importFormats.add(e.getValue()); } return importFormats; } public String getImportFormatShortFormat(String longFormat){ for(java.util.Map.Entry<String,String> e : GlycanDocument.getImportFormats().entrySet()){ if(longFormat.equals(e.getValue())){ return e.getKey(); } } return null; } /** * Add a bracket residue to the structure with the focus */ public void addBracket() { Residue bracket = theDoc.addBracket(getSelectedResiduesList().iterator().next()); if (bracket != null){ setSelection(bracket); documentUpdated(); } } /** * Add a new structure from a terminal motif * * @param name * the identifier of the terminal motif * @see TerminalDictionary */ public void addTerminal(String name) { try { Residue toadd = TerminalDictionary.newTerminal(name); Residue current = getCurrentResidue(); if(theDoc.addResidue(current, getLinkedResidues(), toadd) != null){ setSelection(current); } documentUpdated(); } catch (Exception e) { LogUtils.report(e); } } /** * Return all the structures containing the selected residues and linkages */ public Collection<Glycan> getSelectedStructures() { return theDoc.findStructuresWith(selectedResidues, selectedLinkages); } /** * Return a list containing the selected residues */ public Collection<Residue> getSelectedResiduesList() { return selectedResidues; } // public GlycanRenderer getTheGlycanRenderer() { // return theGlycanRenderer; // } /** * Add a repeating unit containing the selected residues. If the end point * of the unit cannot be easily determined a {@link ResidueSelectorDialog} * is shown */ public void addRepeat() throws Exception{ Collection<Residue> nodes = getSelectedResiduesList(); if (!theDoc.createRepetition(null, nodes)) { Vector<Residue> end_points = new Vector<Residue>(); for(Residue r : nodes){ if(r.isSaccharide()){ end_points.add(r); } } //Glycan structure = getSelectedStructures().iterator().next(); //TODO //ResidueSelectorDialog rsd = new ResidueSelectorDialog( // theParent, "Select ending point", // "Select ending point of the repetition", structure, // end_points, false, getTheGlycanRenderer()); //rsd.setVisible(true); //if (!rsd.isCanceled()) //theDoc.createRepetition(rsd.getCurrentResidue(), // getSelectedResiduesList()); } documentUpdated(); } public void onMoveCCW() { Residue current = getCurrentResidue(); if (current == null || current.getParent() == null) return; Residue parent = current.getParent(); ResAngle cur_pos = posManager.getRelativePosition(current); // try to move the children in the list Residue other = getResidueBefore(parent, current, cur_pos); if (other != null) { moveBefore(parent, current, other); updateAndMantainSelection(); return; } // set preferred position if (!current.hasPreferredPlacement()) setWasSticky(current, posManager.isSticky(current)); ResAngle new_pos = null; ResiduePlacement new_rp = null; if (posManager.isOnBorder(current)) { new_pos = (cur_pos.getIntAngle() == -90) ? new ResAngle(90) : new ResAngle(-90); new_rp = new ResiduePlacement(new_pos, true, false); } else { new_pos = (cur_pos.getIntAngle() == -90) ? new ResAngle(90) : cur_pos.combine(-45); new_rp = (new_pos.getIntAngle() == -90 || new_pos.getIntAngle() == 90) ? new ResiduePlacement( new_pos, false, current.getWasSticky()) : new ResiduePlacement(new_pos, false, false); } setPlacement(current, new_rp); // put residue in the correct order other = getLastResidue(parent, current, new_pos); moveAfter(parent, current, other); updateAndMantainSelection(); } /** * Move clockwise the display position of the residue with the focus */ public void onMoveCW() { Residue current = getCurrentResidue(); if (current == null || current.getParent() == null) return; ResAngle cur_pos = posManager.getRelativePosition(current); Residue parent = current.getParent(); // try to move the children in the list Residue other = getResidueAfter(parent, current, cur_pos); if (other != null) { moveAfter(parent, current, other); updateAndMantainSelection(); return; } // set preferred position if (!current.hasPreferredPlacement()) setWasSticky(current, posManager.isSticky(current)); ResAngle new_pos = null; ResiduePlacement new_rp = null; if (posManager.isOnBorder(current)) { new_pos = (cur_pos.getIntAngle() == -90) ? new ResAngle(90) : new ResAngle(-90); new_rp = new ResiduePlacement(new_pos, true, false); } else { new_pos = (cur_pos.getIntAngle() == 90) ? new ResAngle(-90) : cur_pos.combine(45); new_rp = (new_pos.getIntAngle() == -90 || new_pos.getIntAngle() == 90) ? new ResiduePlacement( new_pos, false, current.getWasSticky()) : new ResiduePlacement(new_pos, false, false); } setPlacement(current, new_rp); // put residue in the correct order other = getFirstResidue(parent, current, new_pos); moveBefore(parent, current, other); updateAndMantainSelection(); } private Residue getResidueBefore(Residue parent, Residue current, ResAngle cur_pos) { Vector<Residue> linked = theBBoxManager.getLinkedResidues(current); for (int i = parent.indexOf(current) - 1; i >= 0; i--) { Residue other = parent.getChildAt(i); if (posManager.getRelativePosition(other).equals(cur_pos) && !linked.contains(other)) return other; } return null; } private Residue getFirstResidue(Residue parent, Residue current, ResAngle cur_pos) { for (int i = 0; i < parent.getNoChildren(); i++) { Residue other = parent.getChildAt(i); if (posManager.getRelativePosition(other).equals(cur_pos) && other != current) return other; } return null; } private Residue getLastResidue(Residue parent, Residue current, ResAngle cur_pos) { for (int i = parent.getNoChildren() - 1; i >= 0; i--) { Residue other = parent.getChildAt(i); if (posManager.getRelativePosition(other).equals(cur_pos) && other != current) return other; } return null; } private void moveBefore(Residue parent, Residue current, Residue other) { parent.moveChildBefore(current, other); for (Residue r : theBBoxManager.getLinkedResidues(current)) { if (r.getParent() == parent) parent.moveChildBefore(r, other); } } private void moveAfter(Residue parent, Residue current, Residue other) { parent.moveChildAfter(current, other); for (Residue r : theBBoxManager.getLinkedResidues(current)) { if (r.getParent() == parent) parent.moveChildAfter(r, other); } } private void setPlacement(Residue current, ResiduePlacement new_rp) { current.setPreferredPlacement(new_rp); for (Residue r : theBBoxManager.getLinkedResidues(current)) r.setPreferredPlacement(new_rp); } private void setWasSticky(Residue current, boolean f) { current.setWasSticky(f); for (Residue r : theBBoxManager.getLinkedResidues(current)) r.setWasSticky(f); } // --------------- // visual structure rearrangement private Residue getResidueAfter(Residue parent, Residue current, ResAngle cur_pos) { Vector<Residue> linked = theBBoxManager.getLinkedResidues(current); for (int i = parent.indexOf(current) + 1; i < parent.getNoChildren(); i++) { Residue other = parent.getChildAt(i); if (posManager.getRelativePosition(other).equals(cur_pos) && !linked.contains(other)) return other; } return null; } private void updateAndMantainSelection() { if (currentResidue != null) { Residue old_current = currentResidue; theDoc.fireDocumentChanged(); setSelection(old_current); } else if (currentLinkage != null) { Linkage old_current = currentLinkage; theDoc.fireDocumentChanged(); setSelection(old_current); } } public BuilderWorkspace getWorkspace(){ return theWorkspace; } public void setWorkspace(BuilderWorkspace workspace) { theWorkspace=workspace; } }