AlignmentEditorCopyHandler now sets current action status.
[taxeditor.git] / eu.etaxonomy.taxeditor.molecular / src / main / java / eu / etaxonomy / taxeditor / molecular / editor / AlignmentEditor.java
index 3ec547504255b13a4e5d5de496f6ddc0eea66545..9ed9208f004ca08295ac43525618ce3d394f9082 100644 (file)
@@ -54,6 +54,8 @@ import org.biojava.bio.chromatogram.UnsupportedChromatogramFormatException;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
 import org.eclipse.ui.IActionBars;
 import org.eclipse.ui.IEditorInput;
 import org.eclipse.ui.IEditorPart;
@@ -101,9 +103,8 @@ public class AlignmentEditor extends EditorPart {
        public static final String DEFAULT_READ_NAME_PREFIX = "Read ";
        public static final String CONSENSUS_NAME = "Consensus";
 
-
+       
     private final ConversationHolder conversationHolder;
-
        private final AlignmentModelChangeListener DIRTY_LISTENER = new AlignmentModelChangeListener() {
                                @Override
                                public <T> void afterTokenChange(TokenChangeEvent<T> e) {
@@ -127,19 +128,21 @@ public class AlignmentEditor extends EditorPart {
                                        setDirty();
                                }
                        };
+       private final AlignmentEditorActionUpdater ACTION_UPDATER = new AlignmentEditorActionUpdater();
+                       
 
     private MultipleAlignmentsContainer alignmentsContainer = null;
     private final Map<Integer, SingleReadAlignment> cdmMap = new TreeMap<Integer, SingleReadAlignment>();  //TODO Move this to ContigSequenceDataProvider
     private boolean dirty = false;
 
-
-    public AlignmentEditor() {
+    public AlignmentEditor()
+    {
        super();
        conversationHolder = CdmStore.createConversation();
        //conversationHolder = null;
     }
 
-
+    
     private void refreshToolbarElement(String id) {
                ICommandService commandService =
                                (ICommandService)PlatformUI.getWorkbench().getActiveWorkbenchWindow().getService(ICommandService.class);
@@ -148,36 +151,36 @@ public class AlignmentEditor extends EditorPart {
                }
     }
 
-
+    
     private void registerEditSettingListener(MultipleAlignmentsContainer container) {
-       container.getEditSettings().addListener(new EditSettingsListener() {
-                                       @Override
-                                       public void workingModeChanged(EditSettingsChangeEvent e) {}  // Currently nothing to do
-
-                                       @Override
-                                       public void insertLeftInDataAreaChanged(EditSettingsChangeEvent e) {
-                                               updateStatusBar();
-                                       refreshToolbarElement(ToggleLeftRightInsertionHandler.COMMAND_ID);
-                                       }
+        container.getEditSettings().addListener(new EditSettingsListener() {
+            @Override
+            public void workingModeChanged(EditSettingsChangeEvent e) {}  // Currently nothing to do
+
+            @Override
+            public void insertLeftInDataAreaChanged(EditSettingsChangeEvent e) {
+                updateStatusBar();
+                refreshToolbarElement(ToggleLeftRightInsertionHandler.COMMAND_ID);
+            }
 
-                                       @Override
-                                       public void insertChanged(EditSettingsChangeEvent e) {
-                                               updateStatusBar();
-                                       refreshToolbarElement(ToggleInsertOverwriteHandler.COMMAND_ID);
-                                       }
-                               });
+            @Override
+            public void insertChanged(EditSettingsChangeEvent e) {
+                updateStatusBar();
+                refreshToolbarElement(ToggleInsertOverwriteHandler.COMMAND_ID);
+            }
+        });
     }
 
 
-  private AlignmentArea createIndexArea(MultipleAlignmentsContainer container, AlignmentArea labeledArea) {
+    private AlignmentArea createIndexArea(MultipleAlignmentsContainer container, AlignmentArea labeledArea) {
                AlignmentArea result = new AlignmentArea(container);
                result.setAllowVerticalScrolling(false);
                result.getDataAreas().getTopAreas().add(new SequenceIndexArea(result.getContentArea(), labeledArea));
                return result;
-  }
+    }
 
 
-  private AlignmentArea createEditableAlignmentArea(MultipleAlignmentsContainer container, boolean allowVerticalScrolling) {
+    private AlignmentArea createEditableAlignmentArea(MultipleAlignmentsContainer container, boolean allowVerticalScrolling) {
                AlignmentArea result = new AlignmentArea(container);
                result.setAllowVerticalScrolling(allowVerticalScrolling);
 
@@ -190,50 +193,104 @@ public class AlignmentEditor extends EditorPart {
                return result;
        }
 
-
+    
     private AlignmentArea createConsensusHintArea(MultipleAlignmentsContainer container,
                AlignmentArea labeledArea) {
 
-               AlignmentArea result = new AlignmentArea(container);
-               result.setAllowVerticalScrolling(false);
-               result.getDataAreas().getBottomAreas().add(
-                               new ConsensusSequenceArea(result.getContentArea(), labeledArea));
-               return result;
-         }
-
+               AlignmentArea result = new AlignmentArea(container);
+               result.setAllowVerticalScrolling(false);
+               result.getDataAreas().getBottomAreas().add(
+                               new ConsensusSequenceArea(result.getContentArea(), labeledArea));
+               return result;
+    }
 
+    
     private MultipleAlignmentsContainer getAlignmentsContainer() {
        if (alignmentsContainer == null) {
                alignmentsContainer = new MultipleAlignmentsContainer();
-
+               
                AlignmentAreaList list = alignmentsContainer.getAlignmentAreas();
                AlignmentArea readsArea = createEditableAlignmentArea(alignmentsContainer, true);
+               readsArea.getSelection().addSelectionListener(ACTION_UPDATER);
            list.add(createIndexArea(alignmentsContainer, readsArea));
                list.add(readsArea);  // Make sure READS_AREA_INDEX is correct.
-               list.add(createEditableAlignmentArea(alignmentsContainer, false));  // Make sure COMSENSUS_AREA_INDEX is correct.
+               AlignmentArea editableConsensusArea = createEditableAlignmentArea(alignmentsContainer, false);
+               editableConsensusArea.getSelection().addSelectionListener(ACTION_UPDATER);
+               list.add(editableConsensusArea);  // Make sure COMSENSUS_AREA_INDEX is correct.
                list.add(createConsensusHintArea(alignmentsContainer, readsArea));
-
+               
                registerEditSettingListener(alignmentsContainer);
                }
                return alignmentsContainer;
        }
 
-
+    
     public AlignmentArea getReadsArea() {
        return getAlignmentsContainer().getAlignmentAreas().get(READS_AREA_INDEX);
     }
 
-
-    private AlignmentArea getEditableConsensusArea() {
+    
+    public AlignmentArea getEditableConsensusArea() {
        return getAlignmentsContainer().getAlignmentAreas().get(EDITABLE_CONSENSUS_AREA_INDEX);
     }
-
+    
+    
+    /**
+     * Checks whether {@link #getReadsArea()} or {@link #getEditableConsensusArea()} currently
+     * have the user focus and returns the according component. 
+     * 
+     * @return either the reads or the consensus alignment area or {@code null} if none of these
+     *         components is currently focused
+     */
+    public AlignmentArea getFocusedArea() {
+       AlignmentArea result = getReadsArea();
+       if (hasFocus(result)) {
+               return result;
+       }
+       else {
+               result = getEditableConsensusArea();
+               if (hasFocus(result)) {
+                       return result;
+               }
+               else {
+                       return null;
+               }
+       }
+    }
+    
+    
+    /**
+     * Checks whether the specified alignment area or one of its subcomponents currently has the
+     * focus.
+     * 
+     * @param area the alignment area to be checked (Can only be {@link #getReadsArea()} or 
+     *        {@link #getEditableConsensusArea()}.)
+     * @return {@code true} if the specified component is focused and is either equal to
+     *         {@link #getReadsArea()} or {@link #getEditableConsensusArea()}or {@code false} otherwise
+     */
+    private boolean hasFocus(AlignmentArea area) {
+       return childHasFocus((Composite)area.getToolkitComponent());
+    }
+    
+    
+    public static boolean childHasFocus(Composite parent) {  //TODO Move to bioinfweb.commons.swt
+       return isChildComponent(parent, Display.getCurrent().getFocusControl());
+    }
+    
+    
+    public static boolean isChildComponent(Composite parent, Control child) {  //TODO Move to bioinfweb.commons.swt
+        while ((child != parent) && (child != null)) {
+            child = child.getParent();
+        }
+        return (child == parent); 
+    }
+    
 
     public boolean hasPherogram(int sequenceID) {
         return getReadsArea().getDataAreas().getSequenceAreas(sequenceID).size() > PHEROGRAM_AREA_INDEX;
     }
 
-
+    
     public PherogramArea getPherogramArea(int sequenceID) {
         if (hasPherogram(sequenceID)) {
             return (PherogramArea)getReadsArea().getDataAreas().getSequenceAreas(sequenceID).get(PHEROGRAM_AREA_INDEX);
@@ -243,14 +300,14 @@ public class AlignmentEditor extends EditorPart {
         }
     }
 
-
+    
     private ConsensusSequenceArea getConsensusHintDataArea() {
         return (ConsensusSequenceArea)getAlignmentsContainer().getAlignmentAreas().
                 get(CONSENSUS_HINT_AREA_INDEX).getDataAreas().getBottomAreas().
                 get(CONSENSUS_DATA_AREA_INDEX);
     }
 
-
+    
     @Deprecated  //TODO Remove as soon as testing period is over
     private void createTestContents() {
                // Just for testing:
@@ -274,7 +331,7 @@ public class AlignmentEditor extends EditorPart {
                }
     }
 
-
+    
     private void readCDMData(Sequence sequenceNode) {
        //TODO If called from somewhere else than createPartControl() the editorInput needs to be checked and previous contents need to be cleared (or updated).
 
@@ -310,13 +367,12 @@ public class AlignmentEditor extends EditorPart {
                //TODO Can the consensus sequence also be null? / Should it be created here, if nothing is in the DB?
     }
 
-
-       /* (non-Javadoc)
-     * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
-     */
+    
     @Override
     public void createPartControl(Composite parent) {
                SWTComponentFactory.getInstance().getSWTComponent(getAlignmentsContainer(), parent, SWT.NONE);
+               Display.getCurrent().addFilter(SWT.FocusIn, ACTION_UPDATER);
+               Display.getCurrent().addFilter(SWT.FocusOut, ACTION_UPDATER);
                updateStatusBar();
 
                if (getEditorInput() instanceof AlignmentEditorInput) {
@@ -338,8 +394,16 @@ public class AlignmentEditor extends EditorPart {
                }
        }
 
+    
+    @Override
+       public void dispose() {
+               Display.getCurrent().removeFilter(SWT.FocusIn, ACTION_UPDATER);
+               Display.getCurrent().removeFilter(SWT.FocusOut, ACTION_UPDATER);
+               super.dispose();
+       }
 
-    private void updateStatusBar() {
+
+       private void updateStatusBar() {
         IActionBars bars = getEditorSite().getActionBars();
         bars.getStatusLineManager().setMessage("Edit mode: " +
                        (getReadsArea().getEditSettings().isInsert() ? "Insert" : "Overwrite") + "  " +
@@ -347,7 +411,7 @@ public class AlignmentEditor extends EditorPart {
                        (getReadsArea().getEditSettings().isInsertLeftInDataArea() ? "Left" : "Right"));
     }
 
-
+    
     private SingleReadAlignment.Shift[] convertToCDMShifts(PherogramAreaModel model) {
        Iterator<ShiftChange> iterator = model.shiftChangeIterator();
        List<Shift> shifts = new ArrayList<SingleReadAlignment.Shift>();
@@ -358,10 +422,7 @@ public class AlignmentEditor extends EditorPart {
        return shifts.toArray(new Shift[]{});
     }
 
-
-    /* (non-Javadoc)
-     * @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor)
-     */
+    
     @Override
     public void doSave(IProgressMonitor monitor) {
        if (getEditorInput() instanceof AlignmentEditorInput) {
@@ -398,12 +459,15 @@ public class AlignmentEditor extends EditorPart {
 
                        singleRead.setEditedSequence(stringProvider.getSequence(id));
 
-                       PherogramAreaModel model = getPherogramArea(id).getModel();
-                       singleRead.setReverseComplement(model.getPherogramProvider() instanceof ReverseComplementPherogramProvider);  // Works only if ReverseComplementPherogramProvider instances are not nested.
-                       singleRead.setShifts(convertToCDMShifts(getPherogramArea(id).getModel()));
-                       singleRead.setFirstSeqPosition(model.getFirstSeqPos());
-                       singleRead.setLeftCutPosition(model.getLeftCutPosition());
-                       singleRead.setRightCutPosition(model.getRightCutPosition());
+                       PherogramArea pherogramArea = getPherogramArea(id);
+                       if (pherogramArea != null) {
+                               PherogramAreaModel model = pherogramArea.getModel();
+                               singleRead.setReverseComplement(model.getPherogramProvider() instanceof ReverseComplementPherogramProvider);  // Works only if ReverseComplementPherogramProvider instances are not nested.
+                               singleRead.setShifts(convertToCDMShifts(getPherogramArea(id).getModel()));
+                               singleRead.setFirstSeqPosition(model.getFirstSeqPos());
+                               singleRead.setLeftCutPosition(model.getLeftCutPosition());
+                               singleRead.setRightCutPosition(model.getRightCutPosition());
+                       }
                }
 
                if (!conversationHolder.isBound()) {
@@ -425,56 +489,44 @@ public class AlignmentEditor extends EditorPart {
        }
     }
 
-
-    /* (non-Javadoc)
-     * @see org.eclipse.ui.part.EditorPart#doSaveAs()
-     */
+    
     @Override
     public void doSaveAs() {}
 
-
-    /* (non-Javadoc)
-     * @see org.eclipse.ui.part.EditorPart#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
-     */
+    
     @Override
     public void init(IEditorSite site, IEditorInput input) throws PartInitException {
         setSite(site);
         setInput(input);
     }
 
-
-    /* (non-Javadoc)
-     * @see org.eclipse.ui.part.EditorPart#isDirty()
-     */
+    
     @Override
     public boolean isDirty() {
         return dirty;
     }
 
-
+    
     private void setDirty() {
        dirty = true;
        firePropertyChange(IEditorPart.PROP_DIRTY);
     }
 
-
-    /* (non-Javadoc)
-     * @see org.eclipse.ui.part.EditorPart#isSaveAsAllowed()
-     */
+    
     @Override
     public boolean isSaveAsAllowed() {
         return false;  // "Save as" not allowed.
     }
 
-
+    
     @Override
     public void setFocus() {
-        if(conversationHolder!=null){
+        if(conversationHolder != null){
             conversationHolder.bind();
         }
     }
 
-
+    
     public boolean isInsertMode() {
         return getAlignmentsContainer().getEditSettings().isInsert();
     }
@@ -484,17 +536,17 @@ public class AlignmentEditor extends EditorPart {
         return getAlignmentsContainer().getEditSettings().isInsertLeftInDataArea();
     }
 
-
+    
     public void toggleLeftRightInsertionInPherogram() {
        getAlignmentsContainer().getEditSettings().toggleInsertLeftInDataArea();
     }
 
-
+    
     public void toggleInsertOverwrite() {
        getAlignmentsContainer().getEditSettings().toggleInsert();
     }
 
-
+    
     private String cutPherogram(boolean left) {
         SelectionModel selection = getReadsArea().getSelection();
         if (selection.getCursorHeight() != 1) {
@@ -527,17 +579,17 @@ public class AlignmentEditor extends EditorPart {
         }
     }
 
-
+    
     public String cutPherogramLeft() {
         return cutPherogram(true);
     }
 
-
+    
     public String cutPherogramRight() {
         return cutPherogram(false);
     }
 
-
+    
     public void reverseComplementSelectedSequences() {
        SelectionModel selection = getReadsArea().getSelection();
        AlignmentModel<?> model = getReadsArea().getAlignmentModel();
@@ -554,7 +606,7 @@ public class AlignmentEditor extends EditorPart {
                }
     }
 
-
+    
     /**
      * Recreates the whole consensus sequence from all single read sequences. The previous consensus
      * sequence is overwritte.
@@ -575,7 +627,7 @@ public class AlignmentEditor extends EditorPart {
         model.insertTokensAt(sequenceID, 0, tokens);
     }
 
-
+    
     /**
      * Updates the current consensus sequence by replacing gaps by the according consensus tokens
      * calculated from the single read sequences and extends the consensus sequence if necessary.
@@ -609,7 +661,7 @@ public class AlignmentEditor extends EditorPart {
         }
     }
 
-
+    
        public static PherogramProvider readPherogram(URI uri) throws IOException, UnsupportedChromatogramFormatException {
            PherogramProvider result;
                InputStream stream = uri.toURL().openStream();
@@ -622,7 +674,7 @@ public class AlignmentEditor extends EditorPart {
                return result;
        }
 
-
+       
        private String newReadName() {
                int index = 1;
                while (getReadsArea().getAlignmentModel().sequenceIDByName(DEFAULT_READ_NAME_PREFIX + index)
@@ -633,12 +685,12 @@ public class AlignmentEditor extends EditorPart {
                return DEFAULT_READ_NAME_PREFIX + index;
        }
 
-
+       
     public void addRead(URI pherogramURI, boolean reverseComplemented) throws IOException, UnsupportedChromatogramFormatException {
        addRead(newReadName(), pherogramURI, reverseComplemented, null, null, null, null, null);
     }
 
-
+    
     /**
      * Adds a new sequence with attached phergram data area to the reads alignment.
      * <p>
@@ -689,30 +741,32 @@ public class AlignmentEditor extends EditorPart {
 
                if (tokens != null) {  // If either an edited sequence or a pherogram URI was provided.
                    provider.insertTokensAt(id, 0, tokens);
-               // Create pherogram area:
-               PherogramArea pherogramArea = new PherogramArea(getReadsArea().getContentArea(),
-                       new PherogramAreaModel(pherogramProvider));
-
-               // Set position properties and shifts:
-               PherogramAreaModel model = pherogramArea.getModel();
-               if ((firstSeqPos != null) && (leftCutPos != null)) {
-                   model.setFirstSeqLeftCutPos(firstSeqPos, leftCutPos);
-               }
-               if (rightCutPos != null) {
-                   model.setRightCutPosition(rightCutPos);
-               }
-               if ((shifts != null) && (shifts.length > 0)) {
-                   for (int i = 0; i < shifts.length; i++) {
-                       model.addShiftChange(shifts[i].position, shifts[i].shift);
-                   }
-                   setDirty();
-               }
-
-               // Add pherogram area to GUI:
-               pherogramArea.addMouseListener(new PherogramMouseListener(pherogramArea));
-               getReadsArea().getDataAreas().getSequenceAreas(id).add(pherogramArea);
-               }
 
+                   if (pherogramProvider != null) {
+                       // Create pherogram area:
+                       PherogramArea pherogramArea = new PherogramArea(getReadsArea().getContentArea(),
+                               new PherogramAreaModel(pherogramProvider));
+
+                       // Set position properties and shifts:
+                       PherogramAreaModel model = pherogramArea.getModel();
+                       if ((firstSeqPos != null) && (leftCutPos != null)) {
+                           model.setFirstSeqLeftCutPos(firstSeqPos, leftCutPos);
+                       }
+                       if (rightCutPos != null) {
+                           model.setRightCutPosition(rightCutPos);
+                       }
+                       if ((shifts != null) && (shifts.length > 0)) {
+                           for (int i = 0; i < shifts.length; i++) {
+                               model.addShiftChange(shifts[i].position, shifts[i].shift);
+                           }
+                           setDirty();
+                       }
+
+                       // Add pherogram area to GUI:
+                       pherogramArea.addMouseListener(new PherogramMouseListener(pherogramArea));
+                       getReadsArea().getDataAreas().getSequenceAreas(id).add(pherogramArea);
+                   }
+               }
                return id;
        }
 }
\ No newline at end of file