merge-update from trunk
authorPatric Plitzner <p.plitzner@bgbm.org>
Tue, 30 Jun 2015 13:04:15 +0000 (13:04 +0000)
committerPatric Plitzner <p.plitzner@bgbm.org>
Tue, 30 Jun 2015 13:04:15 +0000 (13:04 +0000)
1  2 
.gitattributes
eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/molecular/AlignmentEditor.java

diff --cc .gitattributes
@@@ -1231,6 -1192,6 +1231,7 @@@ eu.etaxonomy.taxeditor.store/icons/spec
  eu.etaxonomy.taxeditor.store/icons/step_done.gif -text
  eu.etaxonomy.taxeditor.store/icons/swap.gif -text
  eu.etaxonomy.taxeditor.store/icons/swap2.gif -text
++eu.etaxonomy.taxeditor.store/icons/switch_view_type-16x16-32.png -text
  eu.etaxonomy.taxeditor.store/icons/synced.gif -text
  eu.etaxonomy.taxeditor.store/icons/system-file-manager.png -text
  eu.etaxonomy.taxeditor.store/icons/system-users.png -text
index c17ffa4,0000000..2ea4df0
mode 100644,000000..100644
--- /dev/null
@@@ -1,696 -1,0 +1,696 @@@
-                                               singleReadAlignment.getFirstSeqPosition(),
-                                               singleReadAlignment.getLeftCutPosition(),
-                                               singleReadAlignment.getRightCutPosition(),
 +// $Id$
 +/**
 +* Copyright (C) 2014 EDIT
 +* European Distributed Institute of Taxonomy
 +* http://www.e-taxonomy.eu
 +*
 +* The contents of this file are subject to the Mozilla Public License Version 1.1
 +* See LICENSE.TXT at the top of this package for the full license terms.
 +*/
 +package eu.etaxonomy.taxeditor.editor.molecular;
 +
 +
 +import info.bioinfweb.jphyloio.events.TokenSetType;
 +import info.bioinfweb.libralign.alignmentarea.AlignmentArea;
 +import info.bioinfweb.libralign.alignmentarea.selection.SelectionModel;
 +import info.bioinfweb.libralign.alignmentarea.tokenpainter.NucleotideTokenPainter;
 +import info.bioinfweb.libralign.dataarea.implementations.ConsensusSequenceArea;
 +import info.bioinfweb.libralign.dataarea.implementations.SequenceIndexArea;
 +import info.bioinfweb.libralign.dataarea.implementations.pherogram.PherogramArea;
 +import info.bioinfweb.libralign.editsettings.EditSettingsChangeEvent;
 +import info.bioinfweb.libralign.editsettings.EditSettingsListener;
 +import info.bioinfweb.libralign.model.AlignmentModel;
 +import info.bioinfweb.libralign.model.AlignmentModelChangeListener;
 +import info.bioinfweb.libralign.model.AlignmentModelUtils;
 +import info.bioinfweb.libralign.model.adapters.StringAdapter;
 +import info.bioinfweb.libralign.model.events.SequenceChangeEvent;
 +import info.bioinfweb.libralign.model.events.SequenceRenamedEvent;
 +import info.bioinfweb.libralign.model.events.TokenChangeEvent;
 +import info.bioinfweb.libralign.model.implementations.PackedAlignmentModel;
 +import info.bioinfweb.libralign.model.tokenset.BioJavaTokenSet;
 +import info.bioinfweb.libralign.model.tokenset.TokenSet;
 +import info.bioinfweb.libralign.multiplealignments.AlignmentAreaList;
 +import info.bioinfweb.libralign.multiplealignments.MultipleAlignmentsContainer;
 +import info.bioinfweb.libralign.pherogram.model.PherogramAreaModel;
 +import info.bioinfweb.libralign.pherogram.model.ShiftChange;
 +import info.bioinfweb.libralign.pherogram.provider.BioJavaPherogramProvider;
 +import info.bioinfweb.libralign.pherogram.provider.PherogramProvider;
 +import info.bioinfweb.libralign.pherogram.provider.ReverseComplementPherogramProvider;
 +
 +import java.io.File;
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.net.URI;
 +import java.util.ArrayList;
 +import java.util.Collection;
 +import java.util.Collections;
 +import java.util.Iterator;
 +import java.util.Map;
 +import java.util.TreeMap;
 +
 +import org.biojava.bio.chromatogram.ChromatogramFactory;
 +import org.biojava.bio.chromatogram.UnsupportedChromatogramFormatException;
 +import org.biojava3.core.sequence.compound.DNACompoundSet;
 +import org.biojava3.core.sequence.compound.NucleotideCompound;
 +import org.eclipse.core.runtime.IProgressMonitor;
 +import org.eclipse.swt.SWT;
 +import org.eclipse.swt.widgets.Composite;
 +import org.eclipse.ui.IActionBars;
 +import org.eclipse.ui.IEditorInput;
 +import org.eclipse.ui.IEditorPart;
 +import org.eclipse.ui.IEditorSite;
 +import org.eclipse.ui.PartInitException;
 +import org.eclipse.ui.PlatformUI;
 +import org.eclipse.ui.commands.ICommandService;
 +import org.eclipse.ui.part.EditorPart;
 +
 +import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
 +import eu.etaxonomy.cdm.api.service.molecular.ISequenceService;
 +import eu.etaxonomy.cdm.model.media.MediaUtils;
 +import eu.etaxonomy.cdm.model.molecular.Sequence;
 +import eu.etaxonomy.cdm.model.molecular.SequenceString;
 +import eu.etaxonomy.cdm.model.molecular.SingleRead;
 +import eu.etaxonomy.cdm.model.molecular.SingleReadAlignment;
 +import eu.etaxonomy.taxeditor.editor.handler.alignmenteditor.ToggleInsertOverwriteHandler;
 +import eu.etaxonomy.taxeditor.editor.handler.alignmenteditor.ToggleLeftRightInsertionHandler;
 +import eu.etaxonomy.taxeditor.store.CdmStore;
 +
 +
 +
 +/**
 + * Editor component to edit a contig alignment used to combine different overlapping pherograms from Sanger sequencing to
 + * a consensus sequence.
 + * <p>
 + * The contained GUI components used to edit the alignment come from <a href="http://bioinfweb.info/LibrAlign/">LibrAlign</a>.
 + *
 + * @author Ben Stöver
 + * @author pplitzner
 + * @date 04.08.2014
 + */
 +public class AlignmentEditor extends EditorPart {
 +    public static final String ID = "eu.etaxonomy.taxeditor.editor.molecular.AlignmentEditor";
 +
 +      public static final int READS_AREA_INDEX = 1;
 +    public static final int EDITABLE_CONSENSUS_AREA_INDEX = READS_AREA_INDEX + 1;
 +    public static final int CONSENSUS_HINT_AREA_INDEX = EDITABLE_CONSENSUS_AREA_INDEX + 1;
 +      public static final int PHEROGRAM_AREA_INDEX = 0;
 +      public static final int CONSENSUS_DATA_AREA_INDEX = 0;
 +      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) {
 +                                      setDirty();
 +                              }
 +
 +                              @Override
 +                              public <T> void afterSequenceRenamed(SequenceRenamedEvent<T> e) {
 +                                      setDirty();
 +                              }
 +
 +                              @Override
 +                              public <T> void afterSequenceChange(SequenceChangeEvent<T> e) {
 +                                      setDirty();
 +                              }
 +
 +                              @Override
 +                              public <T, U> void afterProviderChanged(AlignmentModel<T> oldProvider,
 +                                              AlignmentModel<U> newProvider) {  // Not expected.
 +
 +                                      setDirty();
 +                              }
 +                      };
 +
 +    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() {
 +      super();
 +      //conversationHolder = CdmStore.createConversation();
 +      conversationHolder = null;
 +    }
 +
 +
 +    private void refreshToolbarElement(String id) {
 +              ICommandService commandService =
 +                              (ICommandService)PlatformUI.getWorkbench().getActiveWorkbenchWindow().getService(ICommandService.class);
 +              if (commandService != null) {
 +                      commandService.refreshElements(id, Collections.EMPTY_MAP);
 +              }
 +    }
 +
 +
 +    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);
 +                                      }
 +
 +                                      @Override
 +                                      public void insertChanged(EditSettingsChangeEvent e) {
 +                                              updateStatusBar();
 +                                      refreshToolbarElement(ToggleInsertOverwriteHandler.COMMAND_ID);
 +                                      }
 +                              });
 +    }
 +
 +
 +  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) {
 +              AlignmentArea result = new AlignmentArea(container);
 +              result.setAllowVerticalScrolling(allowVerticalScrolling);
 +
 +              TokenSet<NucleotideCompound> tokenSet = new BioJavaTokenSet<NucleotideCompound>(
 +                              TokenSetType.DNA, new DNACompoundSet(), true);  //TODO Should NUCLEOTIDE be used instead?
 +              AlignmentModel<NucleotideCompound> provider = new PackedAlignmentModel<NucleotideCompound>(tokenSet);
 +              result.setAlignmentModel(provider, false);
 +              provider.getChangeListeners().add(DIRTY_LISTENER);
 +              result.getPaintSettings().getTokenPainterList().set(0, new NucleotideTokenPainter());
 +
 +              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;
 +        }
 +
 +
 +    private MultipleAlignmentsContainer getAlignmentsContainer() {
 +      if (alignmentsContainer == null) {
 +              alignmentsContainer = new MultipleAlignmentsContainer();
 +
 +              AlignmentAreaList list = alignmentsContainer.getAlignmentAreas();
 +              AlignmentArea readsArea = createEditableAlignmentArea(alignmentsContainer, true);
 +          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.
 +              list.add(createConsensusHintArea(alignmentsContainer, readsArea));
 +
 +              registerEditSettingListener(alignmentsContainer);
 +              }
 +              return alignmentsContainer;
 +      }
 +
 +
 +    public AlignmentArea getReadsArea() {
 +      return getAlignmentsContainer().getAlignmentAreas().get(READS_AREA_INDEX);
 +    }
 +
 +
 +    private AlignmentArea getEditableConsensusArea() {
 +      return getAlignmentsContainer().getAlignmentAreas().get(EDITABLE_CONSENSUS_AREA_INDEX);
 +    }
 +
 +
 +    public PherogramArea getPherogramArea(int sequenceID) {
 +      return (PherogramArea)getReadsArea().getDataAreas().getSequenceAreas(sequenceID).get(
 +              PHEROGRAM_AREA_INDEX);
 +    }
 +
 +
 +    private ConsensusSequenceArea getConsensusHintDataArea() {
 +        return (ConsensusSequenceArea)getAlignmentsContainer().getAlignmentAreas().
 +                get(CONSENSUS_HINT_AREA_INDEX).getDataAreas().getBottomAreas().
 +                get(CONSENSUS_DATA_AREA_INDEX);
 +    }
 +
 +
 +    private void createTestContents() {
 +              // Just for testing:
 +              try {
 +                      addRead(new File("D:/Users/BenStoever/ownCloud/Dokumente/Projekte/EDITor/Quelltexte/LibrAlign branch/Repository/eu.etaxonomy.taxeditor.editor/src/main/resources/AlignmentTestData/JR430_JR-P01.ab1").toURI(), false);
 +            //addRead(new File("D:/Users/BenStoever/ownCloud/Dokumente/Projekte/EDITor/Quelltexte/LibrAlign branch/Repository/eu.etaxonomy.taxeditor.editor/src/main/resources/AlignmentTestData/JR444_JR-P05.ab1").toURI(), false);
 +            addRead(new File("D:/Users/BenStoever/ownCloud/Dokumente/Projekte/EDITor/Quelltexte/LibrAlign branch/Repository/eu.etaxonomy.taxeditor.editor/src/main/resources/AlignmentTestData/Test_qualityScore.scf").toURI(), false);
 +
 +                      // Add test consensus sequence:
 +                      AlignmentModel consensusModel = getEditableConsensusArea().getAlignmentModel();
 +                      int id = consensusModel.addSequence(CONSENSUS_NAME);
 +                      Collection<Object> tokens = new ArrayList<Object>();  // First save tokens in a collection to avoid GUI updated for each token.
 +                      tokens.add(consensusModel.getTokenSet().tokenByKeyChar('A'));
 +                      tokens.add(consensusModel.getTokenSet().tokenByKeyChar('C'));
 +                      tokens.add(consensusModel.getTokenSet().tokenByKeyChar('G'));
 +                      tokens.add(consensusModel.getTokenSet().tokenByKeyChar('T'));
 +                      consensusModel.insertTokensAt(id, 0, tokens);
 +              }
 +              catch (Exception e) {
 +                      throw new RuntimeException(e);
 +              }
 +    }
 +
 +
 +    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).
 +
 +              // Add reads:
 +              for (SingleReadAlignment singleReadAlignment : sequenceNode.getSingleReadAlignments()) {
 +                      try {
 +                              SingleRead pherogramInfo = singleReadAlignment.getSingleRead();
 +                              int id = addRead(pherogramInfo.getPrimer().getLabel(),  //TODO Should the sequence name contain other/additional/alternative data? Can the same string as in the derivative tree be used here?
 +                                              MediaUtils.getFirstMediaRepresentationPart(pherogramInfo.getPherogram()).getUri(),
 +                                              singleReadAlignment.isReverseComplement(),
 +                                              singleReadAlignment.getEditedSequence(),
++                                              singleReadAlignment.getFirstSeqPosition()!=null?singleReadAlignment.getFirstSeqPosition():0,
++                                              singleReadAlignment.getLeftCutPosition()!=null?singleReadAlignment.getLeftCutPosition():0,
++                                              singleReadAlignment.getRightCutPosition()!=null?singleReadAlignment.getRightCutPosition():0,
 +                                              singleReadAlignment.getShifts());
 +                              cdmMap.put(id, singleReadAlignment);
 +                      }
 +                      catch (IOException e) {
 +                              e.printStackTrace();  //TODO Output to user (Possibly collect for all pherograms and display in the end.)
 +                      }
 +                      catch (UnsupportedChromatogramFormatException e) {
 +                              e.printStackTrace();  //TODO Output to user (Possibly collect for all pherograms and display in the end.)
 +                      }
 +              }
 +
 +              // Set consensus sequence:
 +              AlignmentModel consensusProvider = getEditableConsensusArea().getAlignmentModel();
 +              int id = consensusProvider.addSequence(CONSENSUS_NAME);
 +              consensusProvider.insertTokensAt(id, 0, AlignmentModelUtils.charSequenceToTokenList(
 +                              sequenceNode.getConsensusSequence().getString(), consensusProvider.getTokenSet()));
 +              //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) {
 +              getAlignmentsContainer().createSWTWidget(parent, SWT.NONE);
 +              updateStatusBar();
 +
 +              if (getEditorInput() instanceof AlignmentEditorInput) {
 +                      if (((AlignmentEditorInput)getEditorInput()).getSequenceNode() != null) {
 +                          Sequence sequenceNode = ((AlignmentEditorInput)getEditorInput()).getSequenceNode();
 +                      //re-load into the current session if it is already persisted in the DB
 +                      if(sequenceNode!=null && sequenceNode.getId()!=0){
 +                          sequenceNode = CdmStore.getService(ISequenceService.class).load(sequenceNode.getUuid());
 +                      }
 +                              readCDMData(sequenceNode);
 +                      }
 +                      else {
 +                              createTestContents();  // This case will removed after the test phase and an exception should probably be thrown.
 +                      }
 +              }
 +              else {
 +                      throw new IllegalArgumentException("The editor input must have the type " +
 +                                      AlignmentEditorInput.class.getCanonicalName());  //TODO What should be done here?
 +              }
 +      }
 +
 +
 +    private void updateStatusBar() {
 +        IActionBars bars = getEditorSite().getActionBars();
 +        bars.getStatusLineManager().setMessage("Edit mode: " +
 +                      (getReadsArea().getEditSettings().isInsert() ? "Insert" : "Overwrite") + "  " +
 +                      "Insertion in pherogram: " +
 +                      (getReadsArea().getEditSettings().isInsertLeftInDataArea() ? "Left" : "Right"));
 +    }
 +
 +
 +    private SingleReadAlignment.Shift[] convertToCDMShifts(PherogramAreaModel model) {
 +      SingleReadAlignment.Shift[] result = new SingleReadAlignment.Shift[model.getShiftChangeCount()];
 +      Iterator<ShiftChange> iterator = model.shiftChangeIterator();
 +      int pos = 0;
 +      while (iterator.hasNext()) {
 +              ShiftChange shiftChange = iterator.next();
 +              result[pos] = new SingleReadAlignment.Shift(shiftChange.getBaseCallIndex(), shiftChange.getShiftChange());
 +      }
 +      return result;
 +    }
 +
 +
 +    /* (non-Javadoc)
 +     * @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor)
 +     */
 +    @Override
 +    public void doSave(IProgressMonitor monitor) {
 +      if (getEditorInput() instanceof AlignmentEditorInput) {
 +              String taskName = "Saving alignment";
 +            monitor.beginTask(taskName, 3);
 +
 +              Sequence sequenceNode = ((AlignmentEditorInput)getEditorInput()).getSequenceNode();
 +              StringAdapter stringProvider = new StringAdapter(getEditableConsensusArea().getAlignmentModel(), false);  // Throws an exception if a token has more than one character.
 +
 +              // Write consensus sequence:
 +              SequenceString consensusSequenceObj = sequenceNode.getConsensusSequence();
 +              String newConsensusSequence = stringProvider.getSequence(
 +                              getEditableConsensusArea().getAlignmentModel().sequenceIDByName(CONSENSUS_NAME));
 +              if (consensusSequenceObj == null) {
 +                      sequenceNode.setConsensusSequence(SequenceString.NewInstance(newConsensusSequence));
 +              }
 +              else {
 +                      consensusSequenceObj.setString(newConsensusSequence);
 +              }
 +
 +              // Write single reads:
 +              stringProvider.setUnderlyingProvider(getReadsArea().getAlignmentModel());
 +              sequenceNode.getSingleReadAlignments().retainAll(cdmMap.values());  // Remove all reads that are not in the alignment anymore.
 +              Iterator<Integer> iterator = getReadsArea().getAlignmentModel().sequenceIDIterator();
 +              while (iterator.hasNext()) {
 +                      int id = iterator.next();
 +                      SingleReadAlignment singleRead = cdmMap.get(id);
 +                      if (singleRead == null) {
 +                              //TODO Create new read object. => Shall it be allowed to add reads in the alignment editor which are not represented in the CDM tree before the alignment editor is saved?
 +                              //singleRead = SingleReadAlignment.NewInstance(consensusSequence, singleRead, shifts, editedSequence);
 +                      }
 +
 +                      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());
 +              }
 +
 +              if (!conversationHolder.isBound()) {
 +                conversationHolder.bind();
 +            }
 +            monitor.worked(1);
 +
 +            // Commit the conversation and start a new transaction immediately:
 +            conversationHolder.commit(true);
 +            monitor.worked(1);
 +
 +            dirty = false;
 +            monitor.worked(1);
 +            monitor.done();
 +            firePropertyChange(PROP_DIRTY);
 +      }
 +      else {
 +              //TODO Throw exception as soon as testing period which allows unlinked AlignmentEditor is over.
 +      }
 +    }
 +
 +
 +    /* (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){
 +            conversationHolder.bind();
 +        }
 +    }
 +
 +
 +    public boolean isInsertMode() {
 +        return getAlignmentsContainer().getEditSettings().isInsert();
 +    }
 +
 +
 +    public boolean isInsertLeftInPherogram() {
 +        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) {
 +            return "Cutting pherograms is only possible if exactly one row is selected.";
 +        }
 +        else {
 +            PherogramArea pherogramArea =
 +                    getPherogramArea(getReadsArea().getSequenceOrder().idByIndex(selection.getCursorRow()));
 +            if (pherogramArea == null) {
 +                return "There is no pherogram attached to the current sequence.";
 +            }
 +            else {
 +                if (left) {
 +                    if (pherogramArea.setLeftCutPositionBySelection()) {
 +                        return null;
 +                    }
 +                    else {
 +                        return "The left end of the selection lies outside the pherogram attached to this sequence.";
 +                    }
 +                }
 +                else {
 +                    if (pherogramArea.setRightCutPositionBySelection()) {
 +                        return null;
 +                    }
 +                    else {
 +                        return "The right end of the selection lies outside the pherogram attached to this sequence.";
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +
 +    public String cutPherogramLeft() {
 +        return cutPherogram(true);
 +    }
 +
 +
 +    public String cutPherogramRight() {
 +        return cutPherogram(false);
 +    }
 +
 +
 +    public void reverseComplementSelectedSequences() {
 +      SelectionModel selection = getReadsArea().getSelection();
 +      AlignmentModel<?> model = getReadsArea().getAlignmentModel();
 +      for (int row = selection.getFirstRow(); row < selection.getFirstRow() + selection.getCursorHeight(); row++) {
 +                      int sequenceID = getReadsArea().getSequenceOrder().idByIndex(row);
 +                      PherogramArea area = getPherogramArea(sequenceID);
 +                      PherogramAreaModel pherogramAlignmentModel = area.getModel();
 +                      AlignmentModelUtils.reverseComplement(model, sequenceID,
 +                              pherogramAlignmentModel.editableIndexByBaseCallIndex(
 +                                      pherogramAlignmentModel.getLeftCutPosition()).getBeforeValidIndex(),
 +                              pherogramAlignmentModel.editableIndexByBaseCallIndex(
 +                                      pherogramAlignmentModel.getRightCutPosition()).getAfterValidIndex());
 +                      pherogramAlignmentModel.reverseComplement();
 +              }
 +    }
 +
 +
 +    /**
 +     * Recreates the whole consensus sequence from all single read sequences. The previous consensus
 +     * sequence is overwritte.
 +     */
 +    @SuppressWarnings("unchecked")
 +    public <T> void createConsensusSequence() {
 +        ConsensusSequenceArea area = getConsensusHintDataArea();
 +        AlignmentModel<T> model = (AlignmentModel<T>)getEditableConsensusArea().getAlignmentModel();
 +        int sequenceID = model.sequenceIDIterator().next();  // There is always one sequence contained.
 +        int length = getReadsArea().getAlignmentModel().getMaxSequenceLength();
 +
 +        Collection<T> tokens = new ArrayList<T>(length);
 +        for (int column = 0; column < length; column++) {
 +            tokens.add(model.getTokenSet().tokenByRepresentation(area.getConsensusToken(column)));
 +        }
 +
 +        model.removeTokensAt(sequenceID, 0, model.getSequenceLength(sequenceID));
 +        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.
 +     */
 +    @SuppressWarnings("unchecked")
 +    public <T> void updateConsensusSequence() {
 +        ConsensusSequenceArea area = getConsensusHintDataArea();
 +        AlignmentModel<T> model = (AlignmentModel<T>)getEditableConsensusArea().getAlignmentModel();
 +        TokenSet<T> tokenSet = model.getTokenSet();
 +        int sequenceID = model.sequenceIDIterator().next();  // There is always one sequence contained.
 +        int currentConsensusLength = model.getSequenceLength(sequenceID);
 +        int overallLength = getReadsArea().getAlignmentModel().getMaxSequenceLength();
 +
 +        // Replace gaps by new information:
 +        for (int column = 0; column < currentConsensusLength; column++) {
 +            if (tokenSet.isGapToken(model.getTokenAt(sequenceID, column))) {
 +                T newToken = tokenSet.tokenByRepresentation(area.getConsensusToken(column));
 +                if (!tokenSet.isGapToken(newToken)) {
 +                    model.setTokenAt(sequenceID, column, newToken);
 +                }
 +            }
 +        }
 +
 +        // Append additional tokens:
 +        if (overallLength > currentConsensusLength) {
 +            Collection<T> tokens = new ArrayList<T>(overallLength);
 +            for (int column = currentConsensusLength; column < overallLength; column++) {
 +                tokens.add(tokenSet.tokenByRepresentation(area.getConsensusToken(column)));
 +            }
 +            model.appendTokens(sequenceID, tokens);
 +        }
 +    }
 +
 +
 +      public static PherogramProvider readPherogram(URI uri) throws IOException, UnsupportedChromatogramFormatException {
 +          PherogramProvider result;
 +              InputStream stream = uri.toURL().openStream();
 +              try {
 +                      result = new BioJavaPherogramProvider(ChromatogramFactory.create(stream));
 +              }
 +              finally {
 +                      stream.close();
 +              }
 +              return result;
 +      }
 +
 +
 +      private String newReadName() {
 +              int index = 1;
 +              while (getReadsArea().getAlignmentModel().sequenceIDByName(DEFAULT_READ_NAME_PREFIX + index)
 +                              != AlignmentModel.NO_SEQUENCE_FOUND) {
 +
 +                      index++;
 +              }
 +              return DEFAULT_READ_NAME_PREFIX + index;
 +      }
 +
 +
 +    public void addRead(URI pherogramURI, boolean reverseComplemented) throws IOException, UnsupportedChromatogramFormatException {
 +      addRead(newReadName(), pherogramURI, reverseComplemented, null, 0, 0, 0, null);  // Position values will be ignored, if shifts == null.
 +    }
 +
 +
 +    /**
 +     * Adds a new sequence with attached phergram data area to the reads alignment.
 +     * <p>
 +     * If {@code null} is specified as {@code editedSequence} the base call sequence from the pherogram will
 +     * be set as the edited sequence. If {@code null} is specified as {@code shifts} no shifts between the edited
 +     * and the base calls sequence are assumed.
 +     *
 +     * @param name the name of the new sequence
 +     * @param pherogramURI the URI where the associated pherogram file is located
 +     * @param reverseComplemented Specify {@code true} here, if the reverse complement of the pherogram data should
 +     *        be added, {@code false} otherwise.
 +     * @param editedSequence the edited version of the base call sequence (May be {@code null}.)
 +     * @param shifts the alignment information that links the edited and the base call sequence (May be {@code null}.)
 +     * @return the sequence ID of the added read
 +     * @throws IOException if an error occurred when trying to read the pherogram file
 +     * @throws UnsupportedChromatogramFormatException if the format of the pherogram file is not supported
 +     */
 +    public int addRead(String name, URI pherogramURI, boolean reverseComplemented, String editedSequence,
 +            int firstSeqPos, int leftCutPos, int rightCutPos, SingleReadAlignment.Shift[] shifts)
 +            throws IOException, UnsupportedChromatogramFormatException {
 +
 +              AlignmentModel provider = getReadsArea().getAlignmentModel();
 +              PherogramProvider pherogramProvider = readPherogram(pherogramURI);  // Must happen before a sequence is added, because it might throw an exception.
 +              if (reverseComplemented) {
 +                      pherogramProvider = new ReverseComplementPherogramProvider(pherogramProvider);
 +              }
 +
 +        // Create sequence:
 +              provider.addSequence(name);
 +              int id = provider.sequenceIDByName(name);
 +
 +              // Set edited sequence:
 +              Collection<Object> tokens;  // First save tokens in a collection to avoid GUI updated for each token.
 +              if (editedSequence != null) {
 +                      tokens = AlignmentModelUtils.charSequenceToTokenList(editedSequence, provider.getTokenSet());
 +              }
 +              else {  // Copy base call sequence into alignment:
 +                      tokens = new ArrayList<Object>();
 +                      for (int i = 0; i < pherogramProvider.getSequenceLength(); i++) {
 +                              tokens.add(provider.getTokenSet().tokenByKeyChar(
 +                                      pherogramProvider.getBaseCall(i).getUpperedBase().charAt(0)));
 +                      }
 +                      setDirty();
 +              }
 +              provider.insertTokensAt(id, 0, tokens);
 +
 +              // Create pherogram area:
 +              PherogramArea pherogramArea = new PherogramArea(getReadsArea().getContentArea(),
 +                      new PherogramAreaModel(pherogramProvider));
 +
 +              // Set position properties and shifts:
 +              if ((shifts != null) && (shifts.length > 0)) {
 +                      PherogramAreaModel model = pherogramArea.getModel();
 +                  model.setFirstSeqLeftCutPos(firstSeqPos, leftCutPos);
 +            model.setRightCutPosition(rightCutPos);
 +
 +                      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);
 +
 +              // Save source URI:
 +              return id;
 +      }
 +}