d78d0559adf73c359838854a4a859545e57c6427
[taxeditor.git] / eu.etaxonomy.taxeditor.editor / src / main / java / eu / etaxonomy / taxeditor / editor / molecular / AlignmentEditor.java
1 // $Id$
2 /**
3 * Copyright (C) 2014 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
9 */
10 package eu.etaxonomy.taxeditor.editor.molecular;
11
12
13 import info.bioinfweb.libralign.alignmentarea.AlignmentArea;
14 import info.bioinfweb.libralign.alignmentarea.selection.SelectionModel;
15 import info.bioinfweb.libralign.dataarea.implementations.ConsensusSequenceArea;
16 import info.bioinfweb.libralign.dataarea.implementations.SequenceIndexArea;
17 import info.bioinfweb.libralign.dataarea.implementations.pherogram.PherogramAlignmentModel;
18 import info.bioinfweb.libralign.dataarea.implementations.pherogram.PherogramArea;
19 import info.bioinfweb.libralign.dataarea.implementations.pherogram.ShiftChange;
20 import info.bioinfweb.libralign.editsettings.EditSettingsChangeEvent;
21 import info.bioinfweb.libralign.editsettings.EditSettingsListener;
22 import info.bioinfweb.libralign.multiplealignments.AlignmentAreaList;
23 import info.bioinfweb.libralign.multiplealignments.MultipleAlignmentsContainer;
24 import info.bioinfweb.libralign.pherogram.provider.BioJavaPherogramProvider;
25 import info.bioinfweb.libralign.pherogram.provider.PherogramProvider;
26 import info.bioinfweb.libralign.pherogram.provider.ReverseComplementPherogramProvider;
27 import info.bioinfweb.libralign.sequenceprovider.SequenceDataChangeListener;
28 import info.bioinfweb.libralign.sequenceprovider.SequenceDataProvider;
29 import info.bioinfweb.libralign.sequenceprovider.SequenceUtils;
30 import info.bioinfweb.libralign.sequenceprovider.adapters.StringAdapter;
31 import info.bioinfweb.libralign.sequenceprovider.events.SequenceChangeEvent;
32 import info.bioinfweb.libralign.sequenceprovider.events.SequenceRenamedEvent;
33 import info.bioinfweb.libralign.sequenceprovider.events.TokenChangeEvent;
34 import info.bioinfweb.libralign.sequenceprovider.implementations.PackedSequenceDataProvider;
35 import info.bioinfweb.libralign.sequenceprovider.tokenset.BioJavaTokenSet;
36 import info.bioinfweb.libralign.sequenceprovider.tokenset.TokenSet;
37
38 import java.io.File;
39 import java.io.IOException;
40 import java.io.InputStream;
41 import java.net.URI;
42 import java.util.ArrayList;
43 import java.util.Collection;
44 import java.util.Collections;
45 import java.util.Iterator;
46 import java.util.Map;
47 import java.util.TreeMap;
48
49 import org.biojava.bio.chromatogram.ChromatogramFactory;
50 import org.biojava.bio.chromatogram.UnsupportedChromatogramFormatException;
51 import org.biojava3.core.sequence.compound.DNACompoundSet;
52 import org.biojava3.core.sequence.compound.NucleotideCompound;
53 import org.eclipse.core.runtime.IProgressMonitor;
54 import org.eclipse.swt.SWT;
55 import org.eclipse.swt.widgets.Composite;
56 import org.eclipse.ui.IActionBars;
57 import org.eclipse.ui.IEditorInput;
58 import org.eclipse.ui.IEditorPart;
59 import org.eclipse.ui.IEditorSite;
60 import org.eclipse.ui.PartInitException;
61 import org.eclipse.ui.PlatformUI;
62 import org.eclipse.ui.commands.ICommandService;
63 import org.eclipse.ui.part.EditorPart;
64
65 import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
66 import eu.etaxonomy.cdm.api.service.molecular.ISequenceService;
67 import eu.etaxonomy.cdm.model.media.MediaUtils;
68 import eu.etaxonomy.cdm.model.molecular.Sequence;
69 import eu.etaxonomy.cdm.model.molecular.SequenceString;
70 import eu.etaxonomy.cdm.model.molecular.SingleRead;
71 import eu.etaxonomy.cdm.model.molecular.SingleReadAlignment;
72 import eu.etaxonomy.taxeditor.editor.handler.alignmenteditor.ToggleInsertOverwriteHandler;
73 import eu.etaxonomy.taxeditor.editor.handler.alignmenteditor.ToggleLeftRightInsertionHandler;
74 import eu.etaxonomy.taxeditor.store.CdmStore;
75
76
77
78 /**
79 * Editor component to edit a contig alignment used to combine different overlapping pherograms from Sanger sequencing to
80 * a consensus sequence.
81 * <p>
82 * The contained GUI components used to edit the alignment come from <a href="http://bioinfweb.info/LibrAlign/">LibrAlign</a>.
83 *
84 * @author Ben Stöver
85 * @author pplitzner
86 * @date 04.08.2014
87 */
88 public class AlignmentEditor extends EditorPart {
89 public static final String ID = "eu.etaxonomy.taxeditor.editor.molecular.AlignmentEditor";
90
91 public static final int READS_AREA_INDEX = 1;
92 public static final int CONSENSUS_AREA_INDEX = READS_AREA_INDEX + 1;
93 public static final int PHEROGRAM_AREA_INDEX = 0;
94 public static final String DEFAULT_READ_NAME_PREFIX = "Read ";
95 public static final String CONSENSUS_NAME = "Consensus";
96
97
98 private final ConversationHolder conversationHolder;
99
100 private final SequenceDataChangeListener DIRTY_LISTENER = new SequenceDataChangeListener() {
101 @Override
102 public <T> void afterTokenChange(TokenChangeEvent<T> e) {
103 setDirty();
104 }
105
106 @Override
107 public <T> void afterSequenceRenamed(SequenceRenamedEvent<T> e) {
108 setDirty();
109 }
110
111 @Override
112 public <T> void afterSequenceChange(SequenceChangeEvent<T> e) {
113 setDirty();
114 }
115
116 @Override
117 public <T, U> void afterProviderChanged(SequenceDataProvider<T> oldProvider,
118 SequenceDataProvider<U> newProvider) { // Not expected.
119
120 setDirty();
121 }
122 };
123
124 private MultipleAlignmentsContainer alignmentsContainer = null;
125 private final Map<Integer, SingleReadAlignment> cdmMap = new TreeMap<Integer, SingleReadAlignment>(); //TODO Move this to ContigSequenceDataProvider
126 private boolean dirty = false;
127
128 public AlignmentEditor() {
129 conversationHolder = CdmStore.createConversation();
130 }
131
132
133 private void refreshToolbarElement(String id) {
134 ICommandService commandService =
135 (ICommandService)PlatformUI.getWorkbench().getActiveWorkbenchWindow().getService(ICommandService.class);
136 if (commandService != null) {
137 commandService.refreshElements(id, Collections.EMPTY_MAP);
138 }
139 }
140
141
142 private void registerEditSettingListener(MultipleAlignmentsContainer container) {
143 container.getEditSettings().addListener(new EditSettingsListener() {
144 @Override
145 public void workingModeChanged(EditSettingsChangeEvent e) {} // Currently nothing to do
146
147 @Override
148 public void insertLeftInDataAreaChanged(EditSettingsChangeEvent e) {
149 updateStatusBar();
150 refreshToolbarElement(ToggleLeftRightInsertionHandler.COMMAND_ID);
151 }
152
153 @Override
154 public void insertChanged(EditSettingsChangeEvent e) {
155 updateStatusBar();
156 refreshToolbarElement(ToggleInsertOverwriteHandler.COMMAND_ID);
157 }
158 });
159 }
160
161
162 private AlignmentArea createIndexArea(MultipleAlignmentsContainer container) {
163 AlignmentArea result = new AlignmentArea(container);
164 result.setAllowVerticalScrolling(false);
165 result.getDataAreas().getTopAreas().add(new SequenceIndexArea(result.getContentArea()));
166 return result;
167 }
168
169
170 private AlignmentArea createEditableAlignmentArea(MultipleAlignmentsContainer container, boolean allowVerticalScrolling) {
171 AlignmentArea result = new AlignmentArea(container);
172 result.setAllowVerticalScrolling(allowVerticalScrolling);
173
174 TokenSet<NucleotideCompound> tokenSet = new BioJavaTokenSet<NucleotideCompound>(new DNACompoundSet(), true);
175 SequenceDataProvider<NucleotideCompound> provider = new PackedSequenceDataProvider<NucleotideCompound>(tokenSet);
176 result.setSequenceProvider(provider, false);
177 provider.getChangeListeners().add(DIRTY_LISTENER);
178
179 return result;
180 }
181
182
183 private AlignmentArea createConsensusHintArea(MultipleAlignmentsContainer container,
184 SequenceDataProvider<?> sequenceProvider) {
185
186 AlignmentArea result = new AlignmentArea(container);
187 result.setAllowVerticalScrolling(false);
188 result.getDataAreas().getBottomAreas().add(
189 new ConsensusSequenceArea(result.getContentArea(), sequenceProvider));
190 return result;
191 }
192
193
194 private MultipleAlignmentsContainer getAlignmentsContainer() {
195 if (alignmentsContainer == null) {
196 alignmentsContainer = new MultipleAlignmentsContainer();
197
198 AlignmentAreaList list = alignmentsContainer.getAlignmentAreas();
199 list.add(createIndexArea(alignmentsContainer));
200 AlignmentArea readsArea = createEditableAlignmentArea(alignmentsContainer, true);
201 list.add(readsArea); // Make sure READS_AREA_INDEX is correct.
202 list.add(createEditableAlignmentArea(alignmentsContainer, false)); // Make sure COMSENSUS_AREA_INDEX is correct.
203 list.add(createConsensusHintArea(alignmentsContainer,
204 readsArea.getSequenceProvider()));
205
206 registerEditSettingListener(alignmentsContainer);
207 }
208 return alignmentsContainer;
209 }
210
211
212 private AlignmentArea getReadsArea() {
213 return getAlignmentsContainer().getAlignmentAreas().get(READS_AREA_INDEX);
214 }
215
216
217 private AlignmentArea getConsensusArea() {
218 return getAlignmentsContainer().getAlignmentAreas().get(CONSENSUS_AREA_INDEX);
219 }
220
221
222 private PherogramArea getPherogramArea(int sequenceID) {
223 return (PherogramArea)getReadsArea().getDataAreas().getSequenceAreas(sequenceID).get(PHEROGRAM_AREA_INDEX);
224 }
225
226
227 private void createTestContents() {
228 // Just for testing:
229 try {
230 addRead(new File("D:/Users/BenStoever/Documents/Studium/Projekte/Promotion/EDITor/Quelltexte/LibrAlign branch/Repository/eu.etaxonomy.taxeditor.editor/src/main/resources/AlignmentTestData/JR430_JR-P01.ab1").toURI(), false);
231 addRead(new File("D:/Users/BenStoever/Documents/Studium/Projekte/Promotion/EDITor/Quelltexte/LibrAlign branch/Repository/eu.etaxonomy.taxeditor.editor/src/main/resources/AlignmentTestData/JR444_JR-P05.ab1").toURI(), false);
232
233 // Add test consensus sequence:
234 SequenceDataProvider consensusProvider = getConsensusArea().getSequenceProvider();
235 int id = consensusProvider.addSequence(CONSENSUS_NAME);
236 Collection<Object> tokens = new ArrayList<Object>(); // First save tokens in a collection to avoid GUI updated for each token.
237 tokens.add(consensusProvider.getTokenSet().tokenByKeyChar('A'));
238 tokens.add(consensusProvider.getTokenSet().tokenByKeyChar('C'));
239 tokens.add(consensusProvider.getTokenSet().tokenByKeyChar('G'));
240 tokens.add(consensusProvider.getTokenSet().tokenByKeyChar('T'));
241 consensusProvider.insertTokensAt(id, 0, tokens);
242 }
243 catch (Exception e) {
244 throw new RuntimeException(e);
245 }
246 }
247
248
249 private void readCDMData(Sequence sequenceNode) {
250 //TODO If called from somewhere else than createPartControl() the editorInput needs to be checked and previous contents need to be cleared (or updated).
251
252 // Add reads:
253 for (SingleReadAlignment singleReadAlignment : sequenceNode.getSingleReadAlignments()) {
254 try {
255 SingleRead pherogramInfo = singleReadAlignment.getSingleRead();
256 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?
257 MediaUtils.getFirstMediaRepresentationPart(pherogramInfo.getPherogram()).getUri(),
258 singleReadAlignment.isReverseComplement(),
259 singleReadAlignment.getEditedSequence(),
260 singleReadAlignment.getShifts());
261 cdmMap.put(id, singleReadAlignment);
262 }
263 catch (IOException e) {
264 e.printStackTrace(); //TODO Output to user (Possibly collect for all pherograms and display in the end.)
265 }
266 catch (UnsupportedChromatogramFormatException e) {
267 e.printStackTrace(); //TODO Output to user (Possibly collect for all pherograms and display in the end.)
268 }
269 }
270
271 // Set consensus sequence:
272 SequenceDataProvider consensusProvider = getConsensusArea().getSequenceProvider();
273 int id = consensusProvider.addSequence(CONSENSUS_NAME);
274 consensusProvider.insertTokensAt(id, 0, SequenceUtils.stringToTokenList(
275 sequenceNode.getConsensusSequence().getString(), consensusProvider.getTokenSet()));
276 //TODO Can the consensus sequence also be null? / Should it be created here, if nothing is in the DB?
277 }
278
279
280 /* (non-Javadoc)
281 * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
282 */
283 @Override
284 public void createPartControl(Composite parent) {
285 getAlignmentsContainer().createSWTWidget(parent, SWT.NONE);
286 updateStatusBar();
287
288 if (getEditorInput() instanceof AlignmentEditorInput) {
289 if (((AlignmentEditorInput)getEditorInput()).getSequenceNode() != null) {
290 Sequence sequenceNode = ((AlignmentEditorInput)getEditorInput()).getSequenceNode();
291 //re-load into the current session if it is already persisted in the DB
292 if(sequenceNode!=null && sequenceNode.getId()!=0){
293 sequenceNode = CdmStore.getService(ISequenceService.class).load(sequenceNode.getUuid());
294 }
295 readCDMData(sequenceNode);
296 }
297 else {
298 createTestContents(); // This case will removed after the test phase and an exception should probably be thrown.
299 }
300 }
301 else {
302 throw new IllegalArgumentException("The editor input must have the type " +
303 AlignmentEditorInput.class.getCanonicalName()); //TODO What should be done here?
304 }
305 }
306
307
308 private void updateStatusBar() {
309 IActionBars bars = getEditorSite().getActionBars();
310 bars.getStatusLineManager().setMessage("Edit mode: " +
311 (getReadsArea().getEditSettings().isInsert() ? "Insert" : "Overwrite") + " " +
312 "Insertion in pherogram: " +
313 (getReadsArea().getEditSettings().isInsertLeftInDataArea() ? "Left" : "Right"));
314 }
315
316
317 private SingleReadAlignment.Shift[] convertToCDMShifts(PherogramAlignmentModel alignmentModel) {
318 SingleReadAlignment.Shift[] result = new SingleReadAlignment.Shift[alignmentModel.getShiftChangeCount()];
319 Iterator<ShiftChange> iterator = alignmentModel.shiftChangeIterator();
320 int pos = 0;
321 while (iterator.hasNext()) {
322 ShiftChange shiftChange = iterator.next();
323 result[pos] = new SingleReadAlignment.Shift(shiftChange.getBaseCallIndex(), shiftChange.getShiftChange());
324 }
325 return result;
326 }
327
328
329 /* (non-Javadoc)
330 * @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor)
331 */
332 @Override
333 public void doSave(IProgressMonitor monitor) {
334 if (getEditorInput() instanceof AlignmentEditorInput) {
335 String taskName = "Saving alignment";
336 monitor.beginTask(taskName, 3);
337
338 Sequence sequenceNode = ((AlignmentEditorInput)getEditorInput()).getSequenceNode();
339 StringAdapter stringProvider = new StringAdapter(getConsensusArea().getSequenceProvider(), false); // Throws an exception if a token has more than one character.
340
341 // Write consensus sequence:
342 SequenceString consensusSequenceObj = sequenceNode.getConsensusSequence();
343 String newConsensusSequence = stringProvider.getSequence(
344 getConsensusArea().getSequenceProvider().sequenceIDByName(CONSENSUS_NAME));
345 if (consensusSequenceObj == null) {
346 sequenceNode.setConsensusSequence(SequenceString.NewInstance(newConsensusSequence));
347 }
348 else {
349 consensusSequenceObj.setString(newConsensusSequence);
350 }
351
352 // Write single reads:
353 stringProvider.setUnderlyingProvider(getReadsArea().getSequenceProvider());
354 sequenceNode.getSingleReadAlignments().retainAll(cdmMap.values()); // Remove all reads that are not in the alignment anymore.
355 Iterator<Integer> iterator = getReadsArea().getSequenceProvider().sequenceIDIterator();
356 while (iterator.hasNext()) {
357 int id = iterator.next();
358 SingleReadAlignment singleRead = cdmMap.get(id);
359 if (singleRead == null) {
360 //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?
361 //singleRead = SingleReadAlignment.NewInstance(consensusSequence, singleRead, shifts, editedSequence);
362 }
363
364 singleRead.setEditedSequence(stringProvider.getSequence(id));
365 singleRead.setReverseComplement(getPherogramArea(id).getProvider() instanceof ReverseComplementPherogramProvider); // Works only if ReverseComplementPherogramProvider instances are not nested.
366 singleRead.setShifts(convertToCDMShifts(getPherogramArea(id).getAlignmentModel()));
367 }
368
369 if (!conversationHolder.isBound()) {
370 conversationHolder.bind();
371 }
372 monitor.worked(1);
373
374 // Commit the conversation and start a new transaction immediately:
375 conversationHolder.commit(true);
376 monitor.worked(1);
377
378 dirty = false;
379 monitor.worked(1);
380 monitor.done();
381 firePropertyChange(PROP_DIRTY);
382 }
383 else {
384 //TODO Throw exception as soon as testing period which allows unlinked AlignmentEditor is over.
385 }
386 }
387
388
389 /* (non-Javadoc)
390 * @see org.eclipse.ui.part.EditorPart#doSaveAs()
391 */
392 @Override
393 public void doSaveAs() {}
394
395
396 /* (non-Javadoc)
397 * @see org.eclipse.ui.part.EditorPart#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
398 */
399 @Override
400 public void init(IEditorSite site, IEditorInput input) throws PartInitException {
401 setSite(site);
402 setInput(input);
403 }
404
405
406 /* (non-Javadoc)
407 * @see org.eclipse.ui.part.EditorPart#isDirty()
408 */
409 @Override
410 public boolean isDirty() {
411 return dirty;
412 }
413
414
415 private void setDirty() {
416 dirty = true;
417 firePropertyChange(IEditorPart.PROP_DIRTY);
418 }
419
420
421 /* (non-Javadoc)
422 * @see org.eclipse.ui.part.EditorPart#isSaveAsAllowed()
423 */
424 @Override
425 public boolean isSaveAsAllowed() {
426 return false; // "Save as" not allowed.
427 }
428
429
430 @Override
431 public void setFocus() {
432 if(conversationHolder!=null){
433 conversationHolder.bind();
434 }
435 }
436
437
438 public boolean isInsertMode() {
439 return getAlignmentsContainer().getEditSettings().isInsert();
440 }
441
442
443 public boolean isInsertLeftInPherogram() {
444 return getAlignmentsContainer().getEditSettings().isInsertLeftInDataArea();
445 }
446
447
448 public void toggleLeftRightInsertionInPherogram() {
449 getAlignmentsContainer().getEditSettings().toggleInsertLeftInDataArea();
450 }
451
452
453 public void toggleInsertOverwrite() {
454 getAlignmentsContainer().getEditSettings().toggleInsert();
455 }
456
457
458 public void reverseComplementSelection() {
459 SelectionModel selection = getReadsArea().getSelection();
460 SequenceDataProvider<?> provider = getReadsArea().getSequenceProvider();
461 for (int row = selection.getStartRow(); row < selection.getStartRow() + selection.getCursorHeight(); row++) {
462 int sequenceID = getReadsArea().getSequenceOrder().idByIndex(row);
463 //TODO rc edited sequence
464
465 if (getPherogramArea(sequenceID).getProvider() instanceof ReverseComplementPherogramProvider) {
466 //getPherogramArea(sequenceID).
467 //TODO Allow to set new provider in PherogramArea or create new PherogramArea
468 //TODO Reposition pherogram according to previous position in edited sequence and length
469 }
470 }
471 }
472
473
474 public static PherogramProvider readPherogram(URI uri) throws IOException, UnsupportedChromatogramFormatException {
475 PherogramProvider result;
476 InputStream stream = uri.toURL().openStream();
477 try {
478 result = new BioJavaPherogramProvider(ChromatogramFactory.create(stream));
479 }
480 finally {
481 stream.close();
482 }
483 return result;
484 }
485
486
487 private String newReadName() {
488 int index = 1;
489 while (getReadsArea().getSequenceProvider().sequenceIDByName(DEFAULT_READ_NAME_PREFIX + index)
490 != SequenceDataProvider.NO_SEQUENCE_FOUND) {
491
492 index++;
493 }
494 return DEFAULT_READ_NAME_PREFIX + index;
495 }
496
497
498 public void addRead(URI pherogramURI, boolean reverseComplemented) throws IOException, UnsupportedChromatogramFormatException {
499 addRead(newReadName(), pherogramURI, reverseComplemented, null, null);
500 }
501
502
503 /**
504 * Adds a new sequence with attached phergram data area to the reads alignment.
505 * <p>
506 * If {@code null} is specified as {@code editedSequence} the base call sequence from the pherogram will
507 * be set as the edited sequence. If {@code null} is specified as {@code shifts} no shifts between the edited
508 * and the base calls sequence are assumed.
509 *
510 * @param name the name of the new sequence
511 * @param pherogramURI the URI where the associated pherogram file is located
512 * @param reverseComplemented Specify {@code true} here, if the reverse complement of the pherogram data should
513 * be added, {@code false} otherwise.
514 * @param editedSequence the edited version of the base call sequence (May be {@code null}.)
515 * @param shifts the alignment information that links the edited and the base call sequence (May be {@code null}.)
516 * @return the sequence ID of the added read
517 * @throws IOException if an error occurred when trying to read the pherogram file
518 * @throws UnsupportedChromatogramFormatException if the format of the pherogram file is not supported
519 */
520 public int addRead(String name, URI pherogramURI, boolean reverseComplemented, String editedSequence,
521 SingleReadAlignment.Shift[] shifts) throws IOException, UnsupportedChromatogramFormatException {
522
523 SequenceDataProvider provider = getReadsArea().getSequenceProvider();
524 PherogramProvider pherogramProvider = readPherogram(pherogramURI); // Must happen before a sequence is added, because it might throw an exception.
525 if (reverseComplemented) {
526 pherogramProvider = new ReverseComplementPherogramProvider(pherogramProvider);
527 }
528
529 // Create sequence:
530 provider.addSequence(name);
531 int id = provider.sequenceIDByName(name);
532
533 // Set edited sequence:
534 Collection<Object> tokens; // First save tokens in a collection to avoid GUI updated for each token.
535 if (editedSequence != null) {
536 tokens = SequenceUtils.stringToTokenList(editedSequence, provider.getTokenSet());
537 }
538 else { // Copy base call sequence into alignment:
539 tokens = new ArrayList<Object>();
540 for (int i = 0; i < pherogramProvider.getSequenceLength(); i++) {
541 tokens.add(provider.getTokenSet().tokenByKeyChar(
542 pherogramProvider.getBaseCall(i).getUpperedBase().charAt(0)));
543 }
544 setDirty();
545 }
546 provider.insertTokensAt(id, 0, tokens);
547
548 // Create pherogram area:
549 PherogramArea pherogramArea = new PherogramArea(getReadsArea().getContentArea(), pherogramProvider);
550
551 // Set shifts:
552 if ((shifts != null) && (shifts.length > 0)) {
553 PherogramAlignmentModel alignmentModel = pherogramArea.getAlignmentModel();
554 for (int i = 0; i < shifts.length; i++) {
555 alignmentModel.addShiftChange(shifts[i].position, shifts[i].shift);
556 }
557 setDirty();
558 }
559
560 // Add pherogram area to GUI:
561 pherogramArea.addMouseListener(new PherogramMouseListener(pherogramURI));
562 getReadsArea().getDataAreas().getSequenceAreas(id).add(pherogramArea);
563
564 // Save source URI:
565 return id;
566 }
567 }