85e6ad59b93b9df4b6d8903e02d726e473beefaa
[jalview.git] / src / jalview / commands / EditCommand.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)
3  * Copyright (C) 2008 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.commands;
20
21 import java.util.*;
22
23 import jalview.datamodel.*;
24
25 /**
26  * 
27  * <p>
28  * Title: EditCommmand
29  * </p>
30  * 
31  * <p>
32  * Description: Essential information for performing undo and redo for cut/paste
33  * insert/delete gap which can be stored in the HistoryList
34  * </p>
35  * 
36  * <p>
37  * Copyright: Copyright (c) 2006
38  * </p>
39  * 
40  * <p>
41  * Company: Dundee University
42  * </p>
43  * 
44  * @author not attributable
45  * @version 1.0
46  */
47 public class EditCommand implements CommandI
48 {
49   public static final int INSERT_GAP = 0;
50
51   public static final int DELETE_GAP = 1;
52
53   public static final int CUT = 2;
54
55   public static final int PASTE = 3;
56
57   public static final int REPLACE = 4;
58
59   Edit[] edits;
60
61   String description;
62
63   public EditCommand()
64   {
65   }
66
67   public EditCommand(String description)
68   {
69     this.description = description;
70   }
71
72   public EditCommand(String description, int command, SequenceI[] seqs,
73           int position, int number, AlignmentI al)
74   {
75     this.description = description;
76     if (command == CUT || command == PASTE)
77     {
78       edits = new Edit[]
79       { new Edit(command, seqs, position, number, al) };
80     }
81
82     performEdit(0, null);
83   }
84
85   public EditCommand(String description, int command, String replace,
86           SequenceI[] seqs, int position, int number, AlignmentI al)
87   {
88     this.description = description;
89     if (command == REPLACE)
90     {
91       edits = new Edit[]
92       { new Edit(command, seqs, position, number, al, replace) };
93     }
94
95     performEdit(0, null);
96   }
97
98   final public String getDescription()
99   {
100     return description;
101   }
102
103   public int getSize()
104   {
105     return edits == null ? 0 : edits.length;
106   }
107
108   final public AlignmentI getAlignment()
109   {
110     return edits[0].al;
111   }
112
113   /**
114    * append a new editCommand Note. this shouldn't be called if the edit is an
115    * operation affects more alignment objects than the one referenced in al (for
116    * example, cut or pasting whole sequences). Use the form with an additional
117    * AlignmentI[] views parameter.
118    * 
119    * @param command
120    * @param seqs
121    * @param position
122    * @param number
123    * @param al
124    * @param performEdit
125    */
126   final public void appendEdit(int command, SequenceI[] seqs, int position,
127           int number, AlignmentI al, boolean performEdit)
128   {
129     appendEdit(command, seqs, position, number, al, performEdit, null);
130   }
131
132   /**
133    * append a new edit command with a set of alignment views that may be
134    * operated on
135    * 
136    * @param command
137    * @param seqs
138    * @param position
139    * @param number
140    * @param al
141    * @param performEdit
142    * @param views
143    */
144   final public void appendEdit(int command, SequenceI[] seqs, int position,
145           int number, AlignmentI al, boolean performEdit, AlignmentI[] views)
146   {
147     Edit edit = new Edit(command, seqs, position, number, al
148             .getGapCharacter());
149     if (al.getHeight() == seqs.length)
150     {
151       edit.al = al;
152       edit.fullAlignmentHeight = true;
153     }
154
155     if (edits != null)
156     {
157       Edit[] temp = new Edit[edits.length + 1];
158       System.arraycopy(edits, 0, temp, 0, edits.length);
159       edits = temp;
160       edits[edits.length - 1] = edit;
161     }
162     else
163     {
164       edits = new Edit[]
165       { edit };
166     }
167
168     if (performEdit)
169     {
170       performEdit(edits.length - 1, views);
171     }
172   }
173
174   final void performEdit(int commandIndex, AlignmentI[] views)
175   {
176     int eSize = edits.length;
177     for (int e = commandIndex; e < eSize; e++)
178     {
179       switch (edits[e].command)
180       {
181       case INSERT_GAP:
182         insertGap(edits[e]);
183         break;
184       case DELETE_GAP:
185         deleteGap(edits[e]);
186         break;
187       case CUT:
188         cut(edits[e], views);
189         break;
190       case PASTE:
191         paste(edits[e], views);
192         break;
193       case REPLACE:
194         replace(edits[e]);
195         break;
196       }
197     }
198   }
199
200   final public void doCommand(AlignmentI[] views)
201   {
202     performEdit(0, views);
203   }
204
205   final public void undoCommand(AlignmentI[] views)
206   {
207     int e = 0, eSize = edits.length;
208     for (e = eSize - 1; e > -1; e--)
209     {
210       switch (edits[e].command)
211       {
212       case INSERT_GAP:
213         deleteGap(edits[e]);
214         break;
215       case DELETE_GAP:
216         insertGap(edits[e]);
217         break;
218       case CUT:
219         paste(edits[e], views);
220         break;
221       case PASTE:
222         cut(edits[e], views);
223         break;
224       case REPLACE:
225         replace(edits[e]);
226         break;
227       }
228     }
229   }
230
231   final void insertGap(Edit command)
232   {
233
234     for (int s = 0; s < command.seqs.length; s++)
235     {
236       command.seqs[s].insertCharAt(command.position, command.number,
237               command.gapChar);
238     }
239
240     adjustAnnotations(command, true, false, null);
241   }
242
243   final void deleteGap(Edit command)
244   {
245     for (int s = 0; s < command.seqs.length; s++)
246     {
247       command.seqs[s].deleteChars(command.position, command.position
248               + command.number);
249     }
250
251     adjustAnnotations(command, false, false, null);
252   }
253
254   void cut(Edit command, AlignmentI[] views)
255   {
256     boolean seqDeleted = false;
257     command.string = new char[command.seqs.length][];
258
259     for (int i = 0; i < command.seqs.length; i++)
260     {
261       if (command.seqs[i].getLength() > command.position)
262       {
263         command.string[i] = command.seqs[i].getSequence(command.position,
264                 command.position + command.number);
265         SequenceI oldds = command.seqs[i].getDatasetSequence();
266         if (command.oldds != null && command.oldds[i] != null)
267         {
268           // we are redoing an undone cut.
269           command.seqs[i].setDatasetSequence(null);
270         }
271         command.seqs[i].deleteChars(command.position, command.position
272                 + command.number);
273         if (command.oldds != null && command.oldds[i] != null)
274         {
275           // oldds entry contains the cut dataset sequence.
276           command.seqs[i].setDatasetSequence(command.oldds[i]);
277           command.oldds[i] = oldds;
278         }
279         else
280         {
281           // modify the oldds if necessary
282           if (oldds != command.seqs[i].getDatasetSequence()
283                   || command.seqs[i].getSequenceFeatures() != null)
284           {
285             if (command.oldds == null)
286             {
287               command.oldds = new SequenceI[command.seqs.length];
288             }
289             command.oldds[i] = oldds;
290             adjustFeatures(command, i, command.seqs[i]
291                     .findPosition(command.position), command.seqs[i]
292                     .findPosition(command.position + command.number), false);
293           }
294         }
295       }
296
297       if (command.seqs[i].getLength() < 1)
298       {
299         command.al.deleteSequence(command.seqs[i]);
300         seqDeleted = true;
301       }
302     }
303
304     adjustAnnotations(command, false, seqDeleted, views);
305   }
306
307   void paste(Edit command, AlignmentI[] views)
308   {
309     StringBuffer tmp;
310     boolean newDSNeeded;
311     boolean newDSWasNeeded;
312     int newstart, newend;
313     boolean seqWasDeleted = false;
314     int start = 0, end = 0;
315
316     for (int i = 0; i < command.seqs.length; i++)
317     {
318       newDSNeeded = false;
319       newDSWasNeeded = command.oldds != null && command.oldds[i] != null;
320       if (command.seqs[i].getLength() < 1)
321       {
322         // ie this sequence was deleted, we need to
323         // read it to the alignment
324         if (command.alIndex[i] < command.al.getHeight())
325         {
326           command.al.getSequences().insertElementAt(command.seqs[i],
327                   command.alIndex[i]);
328         }
329         else
330         {
331           command.al.addSequence(command.seqs[i]);
332         }
333         seqWasDeleted = true;
334       }
335       newstart = command.seqs[i].getStart();
336       newend = command.seqs[i].getEnd();
337
338       tmp = new StringBuffer();
339       tmp.append(command.seqs[i].getSequence());
340       // Undo of a delete does not replace original dataset sequence on to
341       // alignment sequence.
342
343       if (command.string != null && command.string[i] != null)
344       {
345         if (command.position >= tmp.length())
346         {
347           // This occurs if padding is on, and residues
348           // are removed from end of alignment
349           int length = command.position - tmp.length();
350           while (length > 0)
351           {
352             tmp.append(command.gapChar);
353             length--;
354           }
355         }
356         tmp.insert(command.position, command.string[i]);
357         for (int s = 0; s < command.string[i].length; s++)
358         {
359           if (jalview.schemes.ResidueProperties.aaIndex[command.string[i][s]] != 23)
360           {
361             if (!newDSNeeded)
362             {
363               newDSNeeded = true;
364               start = command.seqs[i].findPosition(command.position);
365               end = command.seqs[i].findPosition(command.position
366                       + command.number);
367             }
368             if (command.seqs[i].getStart() == start)
369               newstart--;
370             else
371               newend++;
372           }
373         }
374         command.string[i] = null;
375       }
376
377       command.seqs[i].setSequence(tmp.toString());
378       command.seqs[i].setStart(newstart);
379       command.seqs[i].setEnd(newend);
380       if (newDSNeeded)
381       {
382         if (command.seqs[i].getDatasetSequence() != null)
383         {
384           SequenceI ds;
385           if (newDSWasNeeded)
386           {
387             ds = command.oldds[i];
388           }
389           else
390           {
391             // make a new DS sequence
392             // use new ds mechanism here
393             ds = new Sequence(command.seqs[i].getName(),
394                     jalview.analysis.AlignSeq.extractGaps(
395                             jalview.util.Comparison.GapChars,
396                             command.seqs[i].getSequenceAsString()),
397                     command.seqs[i].getStart(), command.seqs[i].getEnd());
398             ds.setDescription(command.seqs[i].getDescription());
399           }
400           if (command.oldds == null)
401           {
402             command.oldds = new SequenceI[command.seqs.length];
403           }
404           command.oldds[i] = command.seqs[i].getDatasetSequence();
405           command.seqs[i].setDatasetSequence(ds);
406         }
407         adjustFeatures(command, i, start, end, true);
408       }
409     }
410     adjustAnnotations(command, true, seqWasDeleted, views);
411
412     command.string = null;
413   }
414
415   void replace(Edit command)
416   {
417     StringBuffer tmp;
418     String oldstring;
419     int start = command.position;
420     int end = command.number;
421     // TODO TUTORIAL - Fix for replacement with different length of sequence (or
422     // whole sequence)
423     // TODO Jalview 2.4 bugfix change to an aggregate command - original
424     // sequence string is cut, new string is pasted in.
425     command.number = start + command.string[0].length;
426     for (int i = 0; i < command.seqs.length; i++)
427     {
428       /**
429        * cut addHistoryItem(new EditCommand("Cut Sequences", EditCommand.CUT,
430        * cut, sg.getStartRes(), sg.getEndRes()-sg.getStartRes()+1,
431        * viewport.alignment));
432        * 
433        */
434       /**
435        * then addHistoryItem(new EditCommand( "Add sequences",
436        * EditCommand.PASTE, sequences, 0, alignment.getWidth(), alignment) );
437        * 
438        */
439       oldstring = command.seqs[i].getSequenceAsString();
440       tmp = new StringBuffer(oldstring.substring(0, start));
441       tmp.append(command.string[i]);
442       tmp.append(oldstring.substring(end));
443       command.seqs[i].setSequence(tmp.toString());
444       command.string[i] = oldstring.substring(start, end).toCharArray();
445       tmp = null;
446       oldstring = null;
447     }
448   }
449
450   final void adjustAnnotations(Edit command, boolean insert,
451           boolean modifyVisibility, AlignmentI[] views)
452   {
453     AlignmentAnnotation[] annotations = null;
454
455     if (modifyVisibility && !insert)
456     {
457       // only occurs if a sequence was added or deleted.
458       command.deletedAnnotationRows = new Hashtable();
459     }
460     if (command.fullAlignmentHeight)
461     {
462       annotations = command.al.getAlignmentAnnotation();
463     }
464     else
465     {
466       int aSize = 0;
467       AlignmentAnnotation[] tmp;
468       for (int s = 0; s < command.seqs.length; s++)
469       {
470         if (modifyVisibility)
471         {
472           // Rows are only removed or added to sequence object.
473           if (!insert)
474           {
475             // remove rows
476             tmp = command.seqs[s].getAnnotation();
477             if (tmp != null)
478             {
479               int alen = tmp.length;
480               for (int aa = 0; aa < tmp.length; aa++)
481               {
482                 if (!command.al.deleteAnnotation(tmp[aa]))
483                 {
484                   // strip out annotation not in the current al (will be put
485                   // back on insert in all views)
486                   tmp[aa] = null;
487                   alen--;
488                 }
489               }
490               command.seqs[s].setAlignmentAnnotation(null);
491               if (alen != tmp.length)
492               {
493                 // save the non-null annotation references only
494                 AlignmentAnnotation[] saved = new AlignmentAnnotation[alen];
495                 for (int aa = 0, aapos = 0; aa < tmp.length; aa++)
496                 {
497                   if (tmp[aa] != null)
498                   {
499                     saved[aapos++] = tmp[aa];
500                     tmp[aa] = null;
501                   }
502                 }
503                 tmp = saved;
504                 command.deletedAnnotationRows.put(command.seqs[s], saved);
505                 // and then remove any annotation in the other views
506                 for (int alview = 0; views != null && alview < views.length; alview++)
507                 {
508                   if (views[alview] != command.al)
509                   {
510                     AlignmentAnnotation[] toremove = views[alview]
511                             .getAlignmentAnnotation();
512                     if (toremove == null || toremove.length == 0)
513                     {
514                       continue;
515                     }
516                     // remove any alignment annotation on this sequence that's
517                     // on that alignment view.
518                     for (int aa = 0; aa < toremove.length; aa++)
519                     {
520                       if (toremove[aa].sequenceRef == command.seqs[s])
521                       {
522                         views[alview].deleteAnnotation(toremove[aa]);
523                       }
524                     }
525                   }
526                 }
527               }
528               else
529               {
530                 // save all the annotation
531                 command.deletedAnnotationRows.put(command.seqs[s], tmp);
532               }
533             }
534           }
535           else
536           {
537             // recover rows
538             if (command.deletedAnnotationRows != null
539                     && command.deletedAnnotationRows
540                             .containsKey(command.seqs[s]))
541             {
542               AlignmentAnnotation[] revealed = (AlignmentAnnotation[]) command.deletedAnnotationRows
543                       .get(command.seqs[s]);
544               command.seqs[s].setAlignmentAnnotation(revealed);
545               if (revealed != null)
546               {
547                 for (int aa = 0; aa < revealed.length; aa++)
548                 {
549                   // iterate through al adding original annotation
550                   command.al.addAnnotation(revealed[aa]);
551                 }
552                 for (int aa = 0; aa < revealed.length; aa++)
553                 {
554                   command.al.setAnnotationIndex(revealed[aa], aa);
555                 }
556                 // and then duplicate added annotation on every other alignment
557                 // view
558                 for (int vnum = 0; views != null && vnum < views.length; vnum++)
559                 {
560                   if (views[vnum] != command.al)
561                   {
562                     int avwidth = views[vnum].getWidth() + 1;
563                     // duplicate in this view
564                     for (int a = 0; a < revealed.length; a++)
565                     {
566                       AlignmentAnnotation newann = new AlignmentAnnotation(
567                               revealed[a]);
568                       command.seqs[s].addAlignmentAnnotation(newann);
569                       newann.padAnnotation(avwidth);
570                       views[vnum].addAnnotation(newann);
571                       views[vnum].setAnnotationIndex(newann, a);
572                     }
573                   }
574                 }
575               }
576             }
577           }
578           continue;
579         }
580
581         if (command.seqs[s].getAnnotation() == null)
582         {
583           continue;
584         }
585
586         if (aSize == 0)
587         {
588           annotations = command.seqs[s].getAnnotation();
589         }
590         else
591         {
592           tmp = new AlignmentAnnotation[aSize
593                   + command.seqs[s].getAnnotation().length];
594
595           System.arraycopy(annotations, 0, tmp, 0, aSize);
596
597           System.arraycopy(command.seqs[s].getAnnotation(), 0, tmp, aSize,
598                   command.seqs[s].getAnnotation().length);
599
600           annotations = tmp;
601         }
602         aSize = annotations.length;
603       }
604     }
605
606     if (annotations == null)
607     {
608       return;
609     }
610
611     if (!insert)
612     {
613       command.deletedAnnotations = new Hashtable();
614     }
615
616     int aSize;
617     Annotation[] temp;
618     for (int a = 0; a < annotations.length; a++)
619     {
620       if (annotations[a].autoCalculated
621               || annotations[a].annotations == null)
622       {
623         continue;
624       }
625
626       int tSize = 0;
627
628       aSize = annotations[a].annotations.length;
629       if (insert)
630       {
631         temp = new Annotation[aSize + command.number];
632         if (annotations[a].padGaps)
633           for (int aa = 0; aa < temp.length; aa++)
634           {
635             temp[aa] = new Annotation(command.gapChar + "", null, ' ', 0);
636           }
637       }
638       else
639       {
640         if (command.position < aSize)
641         {
642           if (command.position + command.number >= aSize)
643           {
644             tSize = aSize;
645           }
646           else
647           {
648             tSize = aSize - command.number;
649           }
650         }
651         else
652         {
653           tSize = aSize;
654         }
655
656         if (tSize < 0)
657         {
658           tSize = aSize;
659         }
660         temp = new Annotation[tSize];
661       }
662
663       if (insert)
664       {
665         if (command.position < annotations[a].annotations.length)
666         {
667           System.arraycopy(annotations[a].annotations, 0, temp, 0,
668                   command.position);
669
670           if (command.deletedAnnotations != null
671                   && command.deletedAnnotations
672                           .containsKey(annotations[a].annotationId))
673           {
674             Annotation[] restore = (Annotation[]) command.deletedAnnotations
675                     .get(annotations[a].annotationId);
676
677             System.arraycopy(restore, 0, temp, command.position,
678                     command.number);
679
680           }
681
682           System.arraycopy(annotations[a].annotations, command.position,
683                   temp, command.position + command.number, aSize
684                           - command.position);
685         }
686         else
687         {
688           if (command.deletedAnnotations != null
689                   && command.deletedAnnotations
690                           .containsKey(annotations[a].annotationId))
691           {
692             Annotation[] restore = (Annotation[]) command.deletedAnnotations
693                     .get(annotations[a].annotationId);
694
695             temp = new Annotation[annotations[a].annotations.length
696                     + restore.length];
697             System.arraycopy(annotations[a].annotations, 0, temp, 0,
698                     annotations[a].annotations.length);
699             System.arraycopy(restore, 0, temp,
700                     annotations[a].annotations.length, restore.length);
701           }
702           else
703           {
704             temp = annotations[a].annotations;
705           }
706         }
707       }
708       else
709       {
710         if (tSize != aSize || command.position < 2)
711         {
712           int copylen = Math.min(command.position,
713                   annotations[a].annotations.length);
714           if (copylen > 0)
715             System.arraycopy(annotations[a].annotations, 0, temp, 0,
716                     copylen); // command.position);
717
718           Annotation[] deleted = new Annotation[command.number];
719           if (copylen >= command.position)
720           {
721             copylen = Math.min(command.number,
722                     annotations[a].annotations.length - command.position);
723             if (copylen > 0)
724             {
725               System.arraycopy(annotations[a].annotations,
726                       command.position, deleted, 0, copylen); // command.number);
727             }
728           }
729
730           command.deletedAnnotations.put(annotations[a].annotationId,
731                   deleted);
732           if (annotations[a].annotations.length > command.position
733                   + command.number)
734           {
735             System.arraycopy(annotations[a].annotations, command.position
736                     + command.number, temp, command.position,
737                     annotations[a].annotations.length - command.position
738                             - command.number); // aSize
739           }
740         }
741         else
742         {
743           int dSize = aSize - command.position;
744
745           if (dSize > 0)
746           {
747             Annotation[] deleted = new Annotation[command.number];
748             System.arraycopy(annotations[a].annotations, command.position,
749                     deleted, 0, dSize);
750
751             command.deletedAnnotations.put(annotations[a].annotationId,
752                     deleted);
753
754             tSize = Math.min(annotations[a].annotations.length,
755                     command.position);
756             temp = new Annotation[tSize];
757             System.arraycopy(annotations[a].annotations, 0, temp, 0, tSize);
758           }
759           else
760           {
761             temp = annotations[a].annotations;
762           }
763         }
764       }
765
766       annotations[a].annotations = temp;
767     }
768   }
769
770   final void adjustFeatures(Edit command, int index, int i, int j,
771           boolean insert)
772   {
773     SequenceI seq = command.seqs[index];
774     SequenceI sequence = seq.getDatasetSequence();
775     if (sequence == null)
776     {
777       sequence = seq;
778     }
779
780     if (insert)
781     {
782       if (command.editedFeatures != null
783               && command.editedFeatures.containsKey(seq))
784       {
785         sequence
786                 .setSequenceFeatures((SequenceFeature[]) command.editedFeatures
787                         .get(seq));
788       }
789
790       return;
791     }
792
793     SequenceFeature[] sf = sequence.getSequenceFeatures();
794
795     if (sf == null)
796     {
797       return;
798     }
799
800     SequenceFeature[] oldsf = new SequenceFeature[sf.length];
801
802     int cSize = j - i;
803
804     for (int s = 0; s < sf.length; s++)
805     {
806       SequenceFeature copy = new SequenceFeature(sf[s]);
807
808       oldsf[s] = copy;
809
810       if (sf[s].getEnd() < i)
811       {
812         continue;
813       }
814
815       if (sf[s].getBegin() > j)
816       {
817         sf[s].setBegin(copy.getBegin() - cSize);
818         sf[s].setEnd(copy.getEnd() - cSize);
819         continue;
820       }
821
822       if (sf[s].getBegin() >= i)
823       {
824         sf[s].setBegin(i);
825       }
826
827       if (sf[s].getEnd() < j)
828       {
829         sf[s].setEnd(j - 1);
830       }
831
832       sf[s].setEnd(sf[s].getEnd() - (cSize));
833
834       if (sf[s].getBegin() > sf[s].getEnd())
835       {
836         sequence.deleteFeature(sf[s]);
837       }
838     }
839
840     if (command.editedFeatures == null)
841     {
842       command.editedFeatures = new Hashtable();
843     }
844
845     command.editedFeatures.put(seq, oldsf);
846
847   }
848
849   class Edit
850   {
851     public SequenceI[] oldds;
852
853     boolean fullAlignmentHeight = false;
854
855     Hashtable deletedAnnotationRows;
856
857     Hashtable deletedAnnotations;
858
859     Hashtable editedFeatures;
860
861     AlignmentI al;
862
863     int command;
864
865     char[][] string;
866
867     SequenceI[] seqs;
868
869     int[] alIndex;
870
871     int position, number;
872
873     char gapChar;
874
875     Edit(int command, SequenceI[] seqs, int position, int number,
876             char gapChar)
877     {
878       this.command = command;
879       this.seqs = seqs;
880       this.position = position;
881       this.number = number;
882       this.gapChar = gapChar;
883     }
884
885     Edit(int command, SequenceI[] seqs, int position, int number,
886             AlignmentI al)
887     {
888       this.gapChar = al.getGapCharacter();
889       this.command = command;
890       this.seqs = seqs;
891       this.position = position;
892       this.number = number;
893       this.al = al;
894
895       alIndex = new int[seqs.length];
896       for (int i = 0; i < seqs.length; i++)
897       {
898         alIndex[i] = al.findIndex(seqs[i]);
899       }
900
901       fullAlignmentHeight = (al.getHeight() == seqs.length);
902     }
903
904     Edit(int command, SequenceI[] seqs, int position, int number,
905             AlignmentI al, String replace)
906     {
907       this.command = command;
908       this.seqs = seqs;
909       this.position = position;
910       this.number = number;
911       this.al = al;
912       this.gapChar = al.getGapCharacter();
913       string = new char[seqs.length][];
914       for (int i = 0; i < seqs.length; i++)
915       {
916         string[i] = replace.toCharArray();
917       }
918
919       fullAlignmentHeight = (al.getHeight() == seqs.length);
920     }
921   }
922 }