796e3d171d67aa58cd7ff52dd14e78d133febde6
[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
129 public AlignmentEditor() {
130 super();
131 conversationHolder = CdmStore.createConversation();
132 }
133
134
135 private void refreshToolbarElement(String id) {
136 ICommandService commandService =
137 (ICommandService)PlatformUI.getWorkbench().getActiveWorkbenchWindow().getService(ICommandService.class);
138 if (commandService != null) {
139 commandService.refreshElements(id, Collections.EMPTY_MAP);
140 }
141 }
142
143
144 private void registerEditSettingListener(MultipleAlignmentsContainer container) {
145 container.getEditSettings().addListener(new EditSettingsListener() {
146 @Override
147 public void workingModeChanged(EditSettingsChangeEvent e) {} // Currently nothing to do
148
149 @Override
150 public void insertLeftInDataAreaChanged(EditSettingsChangeEvent e) {
151 updateStatusBar();
152 refreshToolbarElement(ToggleLeftRightInsertionHandler.COMMAND_ID);
153 }
154
155 @Override
156 public void insertChanged(EditSettingsChangeEvent e) {
157 updateStatusBar();
158 refreshToolbarElement(ToggleInsertOverwriteHandler.COMMAND_ID);
159 }
160 });
161 }
162
163
164 private AlignmentArea createIndexArea(MultipleAlignmentsContainer container) {
165 AlignmentArea result = new AlignmentArea(container);
166 result.setAllowVerticalScrolling(false);
167 result.getDataAreas().getTopAreas().add(new SequenceIndexArea(result.getContentArea()));
168 return result;
169 }
170
171
172 private AlignmentArea createEditableAlignmentArea(MultipleAlignmentsContainer container, boolean allowVerticalScrolling) {
173 AlignmentArea result = new AlignmentArea(container);
174 result.setAllowVerticalScrolling(allowVerticalScrolling);
175
176 TokenSet<NucleotideCompound> tokenSet = new BioJavaTokenSet<NucleotideCompound>(new DNACompoundSet(), true);
177 SequenceDataProvider<NucleotideCompound> provider = new PackedSequenceDataProvider<NucleotideCompound>(tokenSet);
178 result.setSequenceProvider(provider, false);
179 provider.getChangeListeners().add(DIRTY_LISTENER);
180
181 return result;
182 }
183
184
185 private AlignmentArea createConsensusHintArea(MultipleAlignmentsContainer container,
186 SequenceDataProvider<?> sequenceProvider) {
187
188 AlignmentArea result = new AlignmentArea(container);
189 result.setAllowVerticalScrolling(false);
190 result.getDataAreas().getBottomAreas().add(
191 new ConsensusSequenceArea(result.getContentArea(), sequenceProvider));
192 return result;
193 }
194
195
196 private MultipleAlignmentsContainer getAlignmentsContainer() {
197 if (alignmentsContainer == null) {
198 alignmentsContainer = new MultipleAlignmentsContainer();
199
200 AlignmentAreaList list = alignmentsContainer.getAlignmentAreas();
201 list.add(createIndexArea(alignmentsContainer));
202 AlignmentArea readsArea = createEditableAlignmentArea(alignmentsContainer, true);
203 list.add(readsArea); // Make sure READS_AREA_INDEX is correct.
204 list.add(createEditableAlignmentArea(alignmentsContainer, false)); // Make sure COMSENSUS_AREA_INDEX is correct.
205 list.add(createConsensusHintArea(alignmentsContainer,
206 readsArea.getSequenceProvider()));
207
208 registerEditSettingListener(alignmentsContainer);
209 }
210 return alignmentsContainer;
211 }
212
213
214 private AlignmentArea getReadsArea() {
215 return getAlignmentsContainer().getAlignmentAreas().get(READS_AREA_INDEX);
216 }
217
218
219 private AlignmentArea getConsensusArea() {
220 return getAlignmentsContainer().getAlignmentAreas().get(CONSENSUS_AREA_INDEX);
221 }
222
223
224 private PherogramArea getPherogramArea(int sequenceID) {
225 return (PherogramArea)getReadsArea().getDataAreas().getSequenceAreas(sequenceID).get(PHEROGRAM_AREA_INDEX);
226 }
227
228
229 private void createTestContents() {
230 // Just for testing:
231 try {
232 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);
233 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);
234
235 // Add test consensus sequence:
236 SequenceDataProvider consensusProvider = getConsensusArea().getSequenceProvider();
237 int id = consensusProvider.addSequence(CONSENSUS_NAME);
238 Collection<Object> tokens = new ArrayList<Object>(); // First save tokens in a collection to avoid GUI updated for each token.
239 tokens.add(consensusProvider.getTokenSet().tokenByKeyChar('A'));
240 tokens.add(consensusProvider.getTokenSet().tokenByKeyChar('C'));
241 tokens.add(consensusProvider.getTokenSet().tokenByKeyChar('G'));
242 tokens.add(consensusProvider.getTokenSet().tokenByKeyChar('T'));
243 consensusProvider.insertTokensAt(id, 0, tokens);
244 }
245 catch (Exception e) {
246 throw new RuntimeException(e);
247 }
248 }
249
250
251 private void readCDMData(Sequence sequenceNode) {
252 //TODO If called from somewhere else than createPartControl() the editorInput needs to be checked and previous contents need to be cleared (or updated).
253
254 // Add reads:
255 for (SingleReadAlignment singleReadAlignment : sequenceNode.getSingleReadAlignments()) {
256 try {
257 SingleRead pherogramInfo = singleReadAlignment.getSingleRead();
258 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?
259 MediaUtils.getFirstMediaRepresentationPart(pherogramInfo.getPherogram()).getUri(),
260 singleReadAlignment.isReverseComplement(),
261 singleReadAlignment.getEditedSequence(),
262 singleReadAlignment.getShifts());
263 cdmMap.put(id, singleReadAlignment);
264 }
265 catch (IOException e) {
266 e.printStackTrace(); //TODO Output to user (Possibly collect for all pherograms and display in the end.)
267 }
268 catch (UnsupportedChromatogramFormatException e) {
269 e.printStackTrace(); //TODO Output to user (Possibly collect for all pherograms and display in the end.)
270 }
271 }
272
273 // Set consensus sequence:
274 SequenceDataProvider consensusProvider = getConsensusArea().getSequenceProvider();
275 int id = consensusProvider.addSequence(CONSENSUS_NAME);
276 consensusProvider.insertTokensAt(id, 0, SequenceUtils.stringToTokenList(
277 sequenceNode.getConsensusSequence().getString(), consensusProvider.getTokenSet()));
278 //TODO Can the consensus sequence also be null? / Should it be created here, if nothing is in the DB?
279 }
280
281
282 /* (non-Javadoc)
283 * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
284 */
285 @Override
286 public void createPartControl(Composite parent) {
287 getAlignmentsContainer().createSWTWidget(parent, SWT.NONE);
288 updateStatusBar();
289
290 if (getEditorInput() instanceof AlignmentEditorInput) {
291 if (((AlignmentEditorInput)getEditorInput()).getSequenceNode() != null) {
292 Sequence sequenceNode = ((AlignmentEditorInput)getEditorInput()).getSequenceNode();
293 //re-load into the current session if it is already persisted in the DB
294 if(sequenceNode!=null && sequenceNode.getId()!=0){
295 sequenceNode = CdmStore.getService(ISequenceService.class).load(sequenceNode.getUuid());
296 }
297 readCDMData(sequenceNode);
298 }
299 else {
300 createTestContents(); // This case will removed after the test phase and an exception should probably be thrown.
301 }
302 }
303 else {
304 throw new IllegalArgumentException("The editor input must have the type " +
305 AlignmentEditorInput.class.getCanonicalName()); //TODO What should be done here?
306 }
307 }
308
309
310 private void updateStatusBar() {
311 IActionBars bars = getEditorSite().getActionBars();
312 bars.getStatusLineManager().setMessage("Edit mode: " +
313 (getReadsArea().getEditSettings().isInsert() ? "Insert" : "Overwrite") + " " +
314 "Insertion in pherogram: " +
315 (getReadsArea().getEditSettings().isInsertLeftInDataArea() ? "Left" : "Right"));
316 }
317
318
319 private SingleReadAlignment.Shift[] convertToCDMShifts(PherogramAlignmentModel alignmentModel) {
320 SingleReadAlignment.Shift[] result = new SingleReadAlignment.Shift[alignmentModel.getShiftChangeCount()];
321 Iterator<ShiftChange> iterator = alignmentModel.shiftChangeIterator();
322 int pos = 0;
323 while (iterator.hasNext()) {
324 ShiftChange shiftChange = iterator.next();
325 result[pos] = new SingleReadAlignment.Shift(shiftChange.getBaseCallIndex(), shiftChange.getShiftChange());
326 }
327 return result;
328 }
329
330
331 /* (non-Javadoc)
332 * @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor)
333 */
334 @Override
335 public void doSave(IProgressMonitor monitor) {
336 if (getEditorInput() instanceof AlignmentEditorInput) {
337 String taskName = "Saving alignment";
338 monitor.beginTask(taskName, 3);
339
340 Sequence sequenceNode = ((AlignmentEditorInput)getEditorInput()).getSequenceNode();
341 StringAdapter stringProvider = new StringAdapter(getConsensusArea().getSequenceProvider(), false); // Throws an exception if a token has more than one character.
342
343 // Write consensus sequence:
344 SequenceString consensusSequenceObj = sequenceNode.getConsensusSequence();
345 String newConsensusSequence = stringProvider.getSequence(
346 getConsensusArea().getSequenceProvider().sequenceIDByName(CONSENSUS_NAME));
347 if (consensusSequenceObj == null) {
348 sequenceNode.setConsensusSequence(SequenceString.NewInstance(newConsensusSequence));
349 }
350 else {
351 consensusSequenceObj.setString(newConsensusSequence);
352 }
353
354 // Write single reads:
355 stringProvider.setUnderlyingProvider(getReadsArea().getSequenceProvider());
356 sequenceNode.getSingleReadAlignments().retainAll(cdmMap.values()); // Remove all reads that are not in the alignment anymore.
357 Iterator<Integer> iterator = getReadsArea().getSequenceProvider().sequenceIDIterator();
358 while (iterator.hasNext()) {
359 int id = iterator.next();
360 SingleReadAlignment singleRead = cdmMap.get(id);
361 if (singleRead == null) {
362 //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?
363 //singleRead = SingleReadAlignment.NewInstance(consensusSequence, singleRead, shifts, editedSequence);
364 }
365
366 singleRead.setEditedSequence(stringProvider.getSequence(id));
367 singleRead.setReverseComplement(getPherogramArea(id).getProvider() instanceof ReverseComplementPherogramProvider); // Works only if ReverseComplementPherogramProvider instances are not nested.
368 singleRead.setShifts(convertToCDMShifts(getPherogramArea(id).getAlignmentModel()));
369 }
370
371 if (!conversationHolder.isBound()) {
372 conversationHolder.bind();
373 }
374 monitor.worked(1);
375
376 // Commit the conversation and start a new transaction immediately:
377 conversationHolder.commit(true);
378 monitor.worked(1);
379
380 dirty = false;
381 monitor.worked(1);
382 monitor.done();
383 firePropertyChange(PROP_DIRTY);
384 }
385 else {
386 //TODO Throw exception as soon as testing period which allows unlinked AlignmentEditor is over.
387 }
388 }
389
390
391 /* (non-Javadoc)
392 * @see org.eclipse.ui.part.EditorPart#doSaveAs()
393 */
394 @Override
395 public void doSaveAs() {}
396
397
398 /* (non-Javadoc)
399 * @see org.eclipse.ui.part.EditorPart#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
400 */
401 @Override
402 public void init(IEditorSite site, IEditorInput input) throws PartInitException {
403 setSite(site);
404 setInput(input);
405 }
406
407
408 /* (non-Javadoc)
409 * @see org.eclipse.ui.part.EditorPart#isDirty()
410 */
411 @Override
412 public boolean isDirty() {
413 return dirty;
414 }
415
416
417 private void setDirty() {
418 dirty = true;
419 firePropertyChange(IEditorPart.PROP_DIRTY);
420 }
421
422
423 /* (non-Javadoc)
424 * @see org.eclipse.ui.part.EditorPart#isSaveAsAllowed()
425 */
426 @Override
427 public boolean isSaveAsAllowed() {
428 return false; // "Save as" not allowed.
429 }
430
431
432 @Override
433 public void setFocus() {
434 if(conversationHolder!=null){
435 conversationHolder.bind();
436 }
437 }
438
439
440 public boolean isInsertMode() {
441 return getAlignmentsContainer().getEditSettings().isInsert();
442 }
443
444
445 public boolean isInsertLeftInPherogram() {
446 return getAlignmentsContainer().getEditSettings().isInsertLeftInDataArea();
447 }
448
449
450 public void toggleLeftRightInsertionInPherogram() {
451 getAlignmentsContainer().getEditSettings().toggleInsertLeftInDataArea();
452 }
453
454
455 public void toggleInsertOverwrite() {
456 getAlignmentsContainer().getEditSettings().toggleInsert();
457 }
458
459
460 public void reverseComplementSelection() {
461 SelectionModel selection = getReadsArea().getSelection();
462 SequenceDataProvider<?> provider = getReadsArea().getSequenceProvider();
463 for (int row = selection.getStartRow(); row < selection.getStartRow() + selection.getCursorHeight(); row++) {
464 int sequenceID = getReadsArea().getSequenceOrder().idByIndex(row);
465 //TODO rc edited sequence
466
467 if (getPherogramArea(sequenceID).getProvider() instanceof ReverseComplementPherogramProvider) {
468 //getPherogramArea(sequenceID).
469 //TODO Allow to set new provider in PherogramArea or create new PherogramArea
470 //TODO Reposition pherogram according to previous position in edited sequence and length
471 }
472 }
473 }
474
475
476 public static PherogramProvider readPherogram(URI uri) throws IOException, UnsupportedChromatogramFormatException {
477 PherogramProvider result;
478 InputStream stream = uri.toURL().openStream();
479 try {
480 result = new BioJavaPherogramProvider(ChromatogramFactory.create(stream));
481 }
482 finally {
483 stream.close();
484 }
485 return result;
486 }
487
488
489 private String newReadName() {
490 int index = 1;
491 while (getReadsArea().getSequenceProvider().sequenceIDByName(DEFAULT_READ_NAME_PREFIX + index)
492 != SequenceDataProvider.NO_SEQUENCE_FOUND) {
493
494 index++;
495 }
496 return DEFAULT_READ_NAME_PREFIX + index;
497 }
498
499
500 public void addRead(URI pherogramURI, boolean reverseComplemented) throws IOException, UnsupportedChromatogramFormatException {
501 addRead(newReadName(), pherogramURI, reverseComplemented, null, null);
502 }
503
504
505 /**
506 * Adds a new sequence with attached phergram data area to the reads alignment.
507 * <p>
508 * If {@code null} is specified as {@code editedSequence} the base call sequence from the pherogram will
509 * be set as the edited sequence. If {@code null} is specified as {@code shifts} no shifts between the edited
510 * and the base calls sequence are assumed.
511 *
512 * @param name the name of the new sequence
513 * @param pherogramURI the URI where the associated pherogram file is located
514 * @param reverseComplemented Specify {@code true} here, if the reverse complement of the pherogram data should
515 * be added, {@code false} otherwise.
516 * @param editedSequence the edited version of the base call sequence (May be {@code null}.)
517 * @param shifts the alignment information that links the edited and the base call sequence (May be {@code null}.)
518 * @return the sequence ID of the added read
519 * @throws IOException if an error occurred when trying to read the pherogram file
520 * @throws UnsupportedChromatogramFormatException if the format of the pherogram file is not supported
521 */
522 public int addRead(String name, URI pherogramURI, boolean reverseComplemented, String editedSequence,
523 SingleReadAlignment.Shift[] shifts) throws IOException, UnsupportedChromatogramFormatException {
524
525 SequenceDataProvider provider = getReadsArea().getSequenceProvider();
526 PherogramProvider pherogramProvider = readPherogram(pherogramURI); // Must happen before a sequence is added, because it might throw an exception.
527 if (reverseComplemented) {
528 pherogramProvider = new ReverseComplementPherogramProvider(pherogramProvider);
529 }
530
531 // Create sequence:
532 provider.addSequence(name);
533 int id = provider.sequenceIDByName(name);
534
535 // Set edited sequence:
536 Collection<Object> tokens; // First save tokens in a collection to avoid GUI updated for each token.
537 if (editedSequence != null) {
538 tokens = SequenceUtils.stringToTokenList(editedSequence, provider.getTokenSet());
539 }
540 else { // Copy base call sequence into alignment:
541 tokens = new ArrayList<Object>();
542 for (int i = 0; i < pherogramProvider.getSequenceLength(); i++) {
543 tokens.add(provider.getTokenSet().tokenByKeyChar(
544 pherogramProvider.getBaseCall(i).getUpperedBase().charAt(0)));
545 }
546 setDirty();
547 }
548 provider.insertTokensAt(id, 0, tokens);
549
550 // Create pherogram area:
551 PherogramArea pherogramArea = new PherogramArea(getReadsArea().getContentArea(), pherogramProvider);
552
553 // Set shifts:
554 if ((shifts != null) && (shifts.length > 0)) {
555 PherogramAlignmentModel alignmentModel = pherogramArea.getAlignmentModel();
556 for (int i = 0; i < shifts.length; i++) {
557 alignmentModel.addShiftChange(shifts[i].position, shifts[i].shift);
558 }
559 setDirty();
560 }
561
562 // Add pherogram area to GUI:
563 pherogramArea.addMouseListener(new PherogramMouseListener(pherogramURI));
564 getReadsArea().getDataAreas().getSequenceAreas(id).add(pherogramArea);
565
566 // Save source URI:
567 return id;
568 }
569 }