From: gmungoc Date: Fri, 5 Oct 2018 12:56:33 +0000 (+0100) Subject: JAL-1244 improved status message for insert/delete gap X-Git-Tag: Release_2_11_0~17^2~84^2~2 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=b87e96a7592f21c11e2e3a218b24de524c3a39fc;p=jalview.git JAL-1244 improved status message for insert/delete gap --- diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties index ae5b0e7..3718a83 100644 --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@ -502,6 +502,10 @@ label.edit_name_description = Edit Name/Description... label.create_sequence_feature = Create Sequence Feature... label.edit_sequence = Edit Sequence label.edit_sequences = Edit Sequences +label.insert_gap = Insert 1 gap +label.insert_gaps = Insert {0} gaps +label.delete_gap = Delete 1 gap +label.delete_gaps = Delete {0} gaps label.sequence_details = Sequence Details label.jmol_help = Jmol Help label.chimera_help = Chimera Help diff --git a/resources/lang/Messages_es.properties b/resources/lang/Messages_es.properties index 555977d..8e887b1 100644 --- a/resources/lang/Messages_es.properties +++ b/resources/lang/Messages_es.properties @@ -468,6 +468,10 @@ label.edit_name_description = Editar nombre/descripci label.create_sequence_feature = Crear funciĆ³n de secuencia label.edit_sequence = Editar secuencia label.edit_sequences = Editar secuencias +label.insert_gap = Insertar 1 hueco +label.insert_gaps = Insertar {0} huecos +label.delete_gap = Borrar 1 hueco +label.delete_gaps = Borrar {0} huecos label.sequence_details = Detalles de la secuencia label.jmol_help = Ayuda de Jmol # Todos/Todas is gender-sensitive, but currently only used for feminine (cadena / anotaciĆ³n)! diff --git a/src/jalview/commands/EditCommand.java b/src/jalview/commands/EditCommand.java index cac843f..81076e5 100644 --- a/src/jalview/commands/EditCommand.java +++ b/src/jalview/commands/EditCommand.java @@ -114,7 +114,7 @@ public class EditCommand implements CommandI public abstract Action getUndoAction(); }; - private List edits = new ArrayList(); + private List edits = new ArrayList<>(); String description; @@ -789,7 +789,7 @@ public class EditCommand implements CommandI if (modifyVisibility && !insert) { // only occurs if a sequence was added or deleted. - command.deletedAnnotationRows = new Hashtable(); + command.deletedAnnotationRows = new Hashtable<>(); } if (command.fullAlignmentHeight) { @@ -948,7 +948,7 @@ public class EditCommand implements CommandI if (!insert) { - command.deletedAnnotations = new Hashtable(); + command.deletedAnnotations = new Hashtable<>(); } int aSize; @@ -1138,7 +1138,7 @@ public class EditCommand implements CommandI return; } - List oldsf = new ArrayList(); + List oldsf = new ArrayList<>(); int cSize = j - i; @@ -1196,7 +1196,7 @@ public class EditCommand implements CommandI if (command.editedFeatures == null) { - command.editedFeatures = new Hashtable>(); + command.editedFeatures = new Hashtable<>(); } command.editedFeatures.put(seq, oldsf); @@ -1233,7 +1233,7 @@ public class EditCommand implements CommandI */ public Map priorState(boolean forUndo) { - Map result = new HashMap(); + Map result = new HashMap<>(); if (getEdits() == null) { return result; @@ -1266,7 +1266,7 @@ public class EditCommand implements CommandI * Work backwards through the edit list, deriving the sequences before each * was applied. The final result is the sequence set before any edits. */ - Iterator editList = new ReverseListIterator(getEdits()); + Iterator editList = new ReverseListIterator<>(getEdits()); while (editList.hasNext()) { Edit oldEdit = editList.next(); @@ -1339,6 +1339,12 @@ public class EditCommand implements CommandI char gapChar; + /* + * flag that identifies edits inserted to balance + * user edits in a 'locked editing' region + */ + private boolean systemGenerated; + public Edit(Action cmd, SequenceI[] sqs, int pos, int count, char gap) { @@ -1410,6 +1416,16 @@ public class EditCommand implements CommandI { return gapChar; } + + public void setSystemGenerated(boolean b) + { + systemGenerated = b; + } + + public boolean isSystemGenerated() + { + return systemGenerated; + } } /** @@ -1427,7 +1443,7 @@ public class EditCommand implements CommandI } else { - return new ReverseListIterator(getEdits()); + return new ReverseListIterator<>(getEdits()); } } } diff --git a/src/jalview/gui/SeqPanel.java b/src/jalview/gui/SeqPanel.java index 0135e9d..92a5030 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -1167,9 +1167,32 @@ public class SeqPanel extends JPanel } } - // TODO: Make it more clever than many booleans + /** + * Edits the sequence to insert or delete one or more gaps, in response to a + * mouse drag or cursor mode command. The number of inserts/deletes may be + * specified with the cursor command, or else depends on the mouse event + * (normally one column, but potentially more for a fast mouse drag). + *

+ * Delete gaps is limited to the number of gaps left of the cursor position + * (mouse drag), or at or right of the cursor position (cursor mode). + *

+ * In group editing mode (Ctrl or Cmd down), the edit acts on all sequences in + * the current selection group. + *

+ * In locked editing mode (with a selection group present), inserts/deletions + * within the selection group are limited to its boundaries (and edits outside + * the group stop at its border). + * + * @param insertGap + * true to insert gaps, false to delete gaps + * @param editSeq + * (unused parameter) + * @param startres + * the column at which to perform the action; the number of columns + * affected depends on this.lastres (cursor position) + */ synchronized void editSequence(boolean insertGap, boolean editSeq, - int startres) + final int startres) { int fixedLeft = -1; int fixedRight = -1; @@ -1189,33 +1212,39 @@ public class SeqPanel extends JPanel } /* - * initialise the edit command if there is not - * already one being extended + * make a name for the edit action, for + * status bar message and Undo/Redo menu */ - if (editCommand == null) + String label = null; + if (groupEditing) { - if (groupEditing) - { - editCommand = new EditCommand( - MessageManager.getString("action.edit_group")); - } - else + label = MessageManager.getString("action.edit_group"); + } + else + { + label = seq.getName(); + if (label.length() > 10) { - String label = seq.getName(); - if (label.length() > 10) - { - label = label.substring(0, 10); - } - editCommand = new EditCommand(MessageManager - .formatMessage("label.edit_params", new String[] - { label })); + label = label.substring(0, 10); } + label = MessageManager.formatMessage("label.edit_params", + new String[] + { label }); } + /* + * initialise the edit command if there is not + * already one being extended + */ + if (editCommand == null) + { + editCommand = new EditCommand(label); + } // Are we editing within a selection group? - if (groupEditing || (sg != null - && sg.getSequences(av.getHiddenRepSequences()).contains(seq))) + boolean inSelectionGroup = sg != null + && sg.getSequences(av.getHiddenRepSequences()).contains(seq); + if (groupEditing || inSelectionGroup) { fixedColumns = true; @@ -1285,286 +1314,312 @@ public class SeqPanel extends JPanel } } - if (groupEditing) - { - ap.alignFrame.statusBar.setText(" "); // defer this as difficult! - List vseqs = sg.getSequences(av.getHiddenRepSequences()); - int g, groupSize = vseqs.size(); - SequenceI[] groupSeqs = new SequenceI[groupSize]; - for (g = 0; g < groupSeqs.length; g++) - { - groupSeqs[g] = vseqs.get(g); - } + SequenceI[] seqs = new SequenceI[] { seq }; + + boolean endEditing = false; - // drag to right - if (insertGap) + try + { + if (groupEditing) { - // If the user has selected the whole sequence, and is dragging to - // the right, we can still extend the alignment and selectionGroup - if (sg.getStartRes() == 0 && sg.getEndRes() == fixedRight - && sg.getEndRes() == av.getAlignment().getWidth() - 1) + List vseqs = sg.getSequences(av.getHiddenRepSequences()); + int g, groupSize = vseqs.size(); + SequenceI[] groupSeqs = new SequenceI[groupSize]; + for (g = 0; g < groupSeqs.length; g++) { - sg.setEndRes(av.getAlignment().getWidth() + startres - lastres); - fixedRight = sg.getEndRes(); + groupSeqs[g] = vseqs.get(g); } - // Is it valid with fixed columns?? - // Find the next gap before the end - // of the visible region boundary - boolean blank = false; - for (; fixedRight > lastres; fixedRight--) + // drag to right + if (insertGap) { - blank = true; + // If the user has selected the whole sequence, and is dragging to + // the right, we can still extend the alignment and selectionGroup + if (sg.getStartRes() == 0 && sg.getEndRes() == fixedRight + && sg.getEndRes() == av.getAlignment().getWidth() - 1) + { + sg.setEndRes(av.getAlignment().getWidth() + startres - lastres); + fixedRight = sg.getEndRes(); + } - for (g = 0; g < groupSize; g++) + // Is it valid with fixed columns?? + // Find the next gap before the end + // of the visible region boundary + boolean blank = false; + for (; fixedRight > lastres; fixedRight--) { - for (int j = 0; j < startres - lastres; j++) + blank = true; + + for (g = 0; g < groupSize; g++) { - if (!Comparison.isGap(groupSeqs[g].getCharAt(fixedRight - j))) + for (int j = 0; j < startres - lastres; j++) { - blank = false; - break; + if (!Comparison + .isGap(groupSeqs[g].getCharAt(fixedRight - j))) + { + blank = false; + break; + } } } + if (blank) + { + break; + } } - if (blank) - { - break; - } - } - if (!blank) - { - if (sg.getSize() == av.getAlignment().getHeight()) + if (!blank) { - if ((av.hasHiddenColumns() && startres < av.getAlignment() - .getHiddenColumns() - .getNextHiddenBoundary(false, startres))) + if (sg.getSize() == av.getAlignment().getHeight()) { - endEditing(); - return; - } + if ((av.hasHiddenColumns() + && startres < av.getAlignment().getHiddenColumns() + .getNextHiddenBoundary(false, startres))) + { + endEditing = true; + return; + } - int alWidth = av.getAlignment().getWidth(); - if (av.hasHiddenRows()) - { - int hwidth = av.getAlignment().getHiddenSequences() - .getWidth(); - if (hwidth > alWidth) + int alWidth = av.getAlignment().getWidth(); + if (av.hasHiddenRows()) { - alWidth = hwidth; + int hwidth = av.getAlignment().getHiddenSequences() + .getWidth(); + if (hwidth > alWidth) + { + alWidth = hwidth; + } } + // We can still insert gaps if the selectionGroup + // contains all the sequences + sg.setEndRes(sg.getEndRes() + startres - lastres); + fixedRight = alWidth + startres - lastres; + } + else + { + endEditing = true; + return; } - // We can still insert gaps if the selectionGroup - // contains all the sequences - sg.setEndRes(sg.getEndRes() + startres - lastres); - fixedRight = alWidth + startres - lastres; - } - else - { - endEditing(); - return; } } - } - // drag to left - else if (!insertGap) - { - // / Are we able to delete? - // ie are all columns blank? - - for (g = 0; g < groupSize; g++) + // drag to left + else if (!insertGap) { - for (int j = startres; j < lastres; j++) + // / Are we able to delete? + // ie are all columns blank? + + for (g = 0; g < groupSize; g++) { - if (groupSeqs[g].getLength() <= j) + for (int j = startres; j < lastres; j++) { - continue; - } + if (groupSeqs[g].getLength() <= j) + { + continue; + } - if (!Comparison.isGap(groupSeqs[g].getCharAt(j))) - { - // Not a gap, block edit not valid - endEditing(); - return; + if (!Comparison.isGap(groupSeqs[g].getCharAt(j))) + { + // Not a gap, block edit not valid + endEditing = true; + return; + } } } } - } - if (insertGap) - { - // dragging to the right - if (fixedColumns && fixedRight != -1) + if (insertGap) { - for (int j = lastres; j < startres; j++) + // dragging to the right + if (fixedColumns && fixedRight != -1) { - insertChar(j, groupSeqs, fixedRight); + for (int j = lastres; j < startres; j++) + { + insertGap(j, groupSeqs, fixedRight); + } } - } - else - { - appendEdit(Action.INSERT_GAP, groupSeqs, startres, - startres - lastres); - } - } - else - { - // dragging to the left - if (fixedColumns && fixedRight != -1) - { - for (int j = lastres; j > startres; j--) + else { - deleteChar(startres, groupSeqs, fixedRight); + appendEdit(Action.INSERT_GAP, groupSeqs, startres, + startres - lastres, false); } } else { - appendEdit(Action.DELETE_GAP, groupSeqs, startres, - lastres - startres); - } - - } - } - else - // ///Editing a single sequence/////////// - { - if (fixedRight == -1) - { - String msg = getEditStatusMessage(insertGap, seq.getName()); - ap.alignFrame.statusBar.setText(msg); - } - else - { - ap.alignFrame.statusBar.setText(" "); - } - if (insertGap) - { - // dragging to the right - if (fixedColumns && fixedRight != -1) - { - for (int j = lastres; j < startres; j++) + // dragging to the left + if (fixedColumns && fixedRight != -1) + { + for (int j = lastres; j > startres; j--) + { + deleteChar(startres, groupSeqs, fixedRight); + } + } + else { - insertChar(j, new SequenceI[] { seq }, fixedRight); + appendEdit(Action.DELETE_GAP, groupSeqs, startres, + lastres - startres, false); } } - else - { - appendEdit(Action.INSERT_GAP, new SequenceI[] { seq }, lastres, - startres - lastres); - } } else { - if (!editSeq) + /* + * editing a single sequence + */ + if (insertGap) { - // dragging to the left + // dragging to the right if (fixedColumns && fixedRight != -1) { - for (int j = lastres; j > startres; j--) + for (int j = lastres; j < startres; j++) { - if (!Comparison.isGap(seq.getCharAt(startres))) + if (!insertGap(j, seqs, fixedRight)) { - endEditing(); + /* + * e.g. cursor mode command asked for + * more inserts than are possible + */ + endEditing = true; break; } - deleteChar(startres, new SequenceI[] { seq }, fixedRight); } } else { - // could be a keyboard edit trying to delete none gaps - int max = 0; - for (int m = startres; m < lastres; m++) + appendEdit(Action.INSERT_GAP, seqs, lastres, startres - lastres, + false); + } + } + else + { + if (!editSeq) + { + // dragging to the left + if (fixedColumns && fixedRight != -1) { - if (!Comparison.isGap(seq.getCharAt(m))) + for (int j = lastres; j > startres; j--) { - break; + if (!Comparison.isGap(seq.getCharAt(startres))) + { + endEditing = true; + break; + } + deleteChar(startres, seqs, fixedRight); } - max++; } - - if (max > 0) + else { - appendEdit(Action.DELETE_GAP, new SequenceI[] { seq }, - startres, max); + // could be a keyboard edit trying to delete none gaps + int max = 0; + for (int m = startres; m < lastres; m++) + { + if (!Comparison.isGap(seq.getCharAt(m))) + { + break; + } + max++; + } + if (max > 0) + { + appendEdit(Action.DELETE_GAP, seqs, startres, max, false); + } } } - } - else - {// insertGap==false AND editSeq==TRUE; - if (fixedColumns && fixedRight != -1) - { - for (int j = lastres; j < startres; j++) + else + {// insertGap==false AND editSeq==TRUE; + if (fixedColumns && fixedRight != -1) { - insertChar(j, new SequenceI[] { seq }, fixedRight); + for (int j = lastres; j < startres; j++) + { + insertGap(j, seqs, fixedRight); + } + } + else + { + appendEdit(Action.INSERT_NUC, seqs, lastres, + startres - lastres, false); } - } - else - { - appendEdit(Action.INSERT_NUC, new SequenceI[] { seq }, lastres, - startres - lastres); } } } - } + } finally + { + /* + * report what actually happened (might be less than + * what was requested) + */ + String msg = getEditStatusMessage(editCommand); + ap.alignFrame.statusBar.setText(msg == null ? " " : msg); - lastres = startres; - seqCanvas.repaint(); + if (endEditing) + { + endEditing(); + } + + lastres = startres; + seqCanvas.repaint(); + } } /** * Constructs an informative status bar message while dragging to insert or - * delete gaps + * delete gaps. Answers null if inserts and deletes cancel out. * - * @param insert - * @param seqName + * @param editCommand + * a command containing the list of individual edits * @return */ - protected String getEditStatusMessage(boolean insert, String seqName) + protected static String getEditStatusMessage(EditCommand editCommand) { + if (editCommand == null) + { + return null; + } + /* - * add any inserts, and subtract any deletes, so far + * add any inserts, and subtract any deletes, + * not counting those auto-inserted when doing a 'locked edit' + * (so only counting edits 'under the cursor') */ int count = 0; for (Edit cmd : editCommand.getEdits()) { - count += cmd.getAction() == Action.INSERT_GAP ? cmd.getNumber() - : -cmd.getNumber(); + if (!cmd.isSystemGenerated()) + { + count += cmd.getAction() == Action.INSERT_GAP ? cmd.getNumber() + : -cmd.getNumber(); + } } - /* - * add the current action - */ - count += insert ? 1 : -1; - if (count == 0) { /* * inserts and deletes cancel out */ - return " "; - } - StringBuilder message = new StringBuilder(64); - if (groupEditing) - { - message.append("Edit group:"); - } - else - { - message.append("Edit sequence: ").append(seqName); + return null; } - message.append(count > 0 ? " insert " : " delete "); + String msgKey = count > 1 ? "label.insert_gaps" + : (count == 1 ? "label.insert_gap" + : (count == -1 ? "label.delete_gap" + : "label.delete_gaps")); count = Math.abs(count); - message.append(String.valueOf(count)); - message.append(count > 1 ? " gaps" : " gap"); - String msg = message.toString(); - return msg; + + return MessageManager.formatMessage(msgKey, String.valueOf(count)); } - void insertChar(int j, SequenceI[] seq, int fixedColumn) + /** + * Inserts one gap at column j, deleting the right-most gapped column up to + * (and including) fixedColumn. Returns true if the edit is successful, false + * if no blank column is available to allow the insertion to be balanced by a + * deletion. + * + * @param j + * @param seq + * @param fixedColumn + * @return + */ + boolean insertGap(int j, SequenceI[] seq, int fixedColumn) { int blankColumn = fixedColumn; for (int s = 0; s < seq.length; s++) @@ -1585,40 +1640,53 @@ public class SeqPanel extends JPanel { blankColumn = fixedColumn; endEditing(); - return; + return false; } } - appendEdit(Action.DELETE_GAP, seq, blankColumn, 1); + appendEdit(Action.DELETE_GAP, seq, blankColumn, 1, true); - appendEdit(Action.INSERT_GAP, seq, j, 1); + appendEdit(Action.INSERT_GAP, seq, j, 1, false); + return true; } /** - * Helper method to add and perform one edit action. + * Helper method to add and perform one edit action * * @param action * @param seq * @param pos * @param count + * @param systemGenerated + * true if the edit is a 'balancing' delete (or insert) to match a + * user's insert (or delete) in a locked editing region */ protected void appendEdit(Action action, SequenceI[] seq, int pos, - int count) + int count, boolean systemGenerated) { final Edit edit = new EditCommand().new Edit(action, seq, pos, count, av.getAlignment().getGapCharacter()); + edit.setSystemGenerated(systemGenerated); editCommand.appendEdit(edit, av.getAlignment(), true, null); } - void deleteChar(int j, SequenceI[] seq, int fixedColumn) + /** + * Deletes the character at column j, and inserts a gap at fixedColumn, in + * each of the given sequences. The caller should ensure that all sequences + * are gapped in column j. + * + * @param j + * @param seqs + * @param fixedColumn + */ + void deleteChar(int j, SequenceI[] seqs, int fixedColumn) { + appendEdit(Action.DELETE_GAP, seqs, j, 1, false); - appendEdit(Action.DELETE_GAP, seq, j, 1); - - appendEdit(Action.INSERT_GAP, seq, fixedColumn, 1); + appendEdit(Action.INSERT_GAP, seqs, fixedColumn, 1, true); } /** diff --git a/test/jalview/gui/SeqPanelTest.java b/test/jalview/gui/SeqPanelTest.java index a5d244d..7f3aef1 100644 --- a/test/jalview/gui/SeqPanelTest.java +++ b/test/jalview/gui/SeqPanelTest.java @@ -21,11 +21,16 @@ package jalview.gui; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; +import jalview.commands.EditCommand; +import jalview.commands.EditCommand.Action; +import jalview.commands.EditCommand.Edit; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentI; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceI; +import jalview.util.MessageManager; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -88,4 +93,109 @@ public class SeqPanelTest assertEquals(alignFrame.statusBar.getText(), "Sequence 2 ID: Seq2 Residue: B (2)"); } + + @Test(groups = "Functional") + public void testGetEditStatusMessage() + { + assertNull(SeqPanel.getEditStatusMessage(null)); + + EditCommand edit = new EditCommand(); // empty + assertNull(SeqPanel.getEditStatusMessage(edit)); + + SequenceI[] seqs = new SequenceI[] { new Sequence("a", "b") }; + + // 1 gap + edit.addEdit(edit.new Edit(Action.INSERT_GAP, seqs, 1, 1, '-')); + String expected = MessageManager.formatMessage("label.insert_gap", "1"); + assertEquals(SeqPanel.getEditStatusMessage(edit), expected); + + // 3 more gaps makes +4 + edit.addEdit(edit.new Edit(Action.INSERT_GAP, seqs, 1, 3, '-')); + expected = MessageManager.formatMessage("label.insert_gaps", "4"); + assertEquals(SeqPanel.getEditStatusMessage(edit), expected); + + // 2 deletes makes + 2 + edit.addEdit(edit.new Edit(Action.DELETE_GAP, seqs, 1, 2, '-')); + expected = MessageManager.formatMessage("label.insert_gaps", "2"); + assertEquals(SeqPanel.getEditStatusMessage(edit), expected); + + // 2 more deletes makes 0 - no text + edit.addEdit(edit.new Edit(Action.DELETE_GAP, seqs, 1, 2, '-')); + assertNull(SeqPanel.getEditStatusMessage(edit)); + + // 1 more delete makes 1 delete + edit.addEdit(edit.new Edit(Action.DELETE_GAP, seqs, 1, 1, '-')); + expected = MessageManager.formatMessage("label.delete_gap", "1"); + assertEquals(SeqPanel.getEditStatusMessage(edit), expected); + + // 1 more delete makes 2 deletes + edit.addEdit(edit.new Edit(Action.DELETE_GAP, seqs, 1, 1, '-')); + expected = MessageManager.formatMessage("label.delete_gaps", "2"); + assertEquals(SeqPanel.getEditStatusMessage(edit), expected); + } + + /** + * Tests that simulate 'locked editing', where an inserted gap is balanced by + * a gap deletion in the selection group, and vice versa + */ + @Test(groups = "Functional") + public void testGetEditStatusMessage_lockedEditing() + { + EditCommand edit = new EditCommand(); // empty + SequenceI[] seqs = new SequenceI[] { new Sequence("a", "b") }; + + // 1 gap inserted, balanced by 1 delete + Edit e1 = edit.new Edit(Action.INSERT_GAP, seqs, 1, 1, '-'); + edit.addEdit(e1); + Edit e2 = edit.new Edit(Action.DELETE_GAP, seqs, 5, 1, '-'); + e2.setSystemGenerated(true); + edit.addEdit(e2); + String expected = MessageManager.formatMessage("label.insert_gap", "1"); + assertEquals(SeqPanel.getEditStatusMessage(edit), expected); + + // 2 more gaps makes +3 + Edit e3 = edit.new Edit(Action.INSERT_GAP, seqs, 1, 2, '-'); + edit.addEdit(e3); + Edit e4 = edit.new Edit(Action.DELETE_GAP, seqs, 5, 2, '-'); + e4.setSystemGenerated(true); + edit.addEdit(e4); + expected = MessageManager.formatMessage("label.insert_gaps", "3"); + assertEquals(SeqPanel.getEditStatusMessage(edit), expected); + + // 2 deletes makes + 1 + Edit e5 = edit.new Edit(Action.DELETE_GAP, seqs, 1, 2, '-'); + edit.addEdit(e5); + Edit e6 = edit.new Edit(Action.INSERT_GAP, seqs, 5, 2, '-'); + e6.setSystemGenerated(true); + edit.addEdit(e6); + expected = MessageManager.formatMessage("label.insert_gap", "1"); + assertEquals(SeqPanel.getEditStatusMessage(edit), expected); + + // 1 more delete makes 0 - no text + Edit e7 = edit.new Edit(Action.DELETE_GAP, seqs, 1, 1, '-'); + edit.addEdit(e7); + Edit e8 = edit.new Edit(Action.INSERT_GAP, seqs, 5, 1, '-'); + e8.setSystemGenerated(true); + edit.addEdit(e8); + expected = MessageManager.formatMessage("label.insert_gaps", "2"); + assertNull(SeqPanel.getEditStatusMessage(edit)); + + // 1 more delete makes 1 delete + Edit e9 = edit.new Edit(Action.DELETE_GAP, seqs, 1, 1, '-'); + edit.addEdit(e9); + Edit e10 = edit.new Edit(Action.INSERT_GAP, seqs, 5, 1, '-'); + e10.setSystemGenerated(true); + edit.addEdit(e10); + expected = MessageManager.formatMessage("label.delete_gap", "1"); + assertEquals(SeqPanel.getEditStatusMessage(edit), expected); + + // 2 more deletes makes 3 deletes + Edit e11 = edit.new Edit(Action.DELETE_GAP, seqs, 1, 2, '-'); + edit.addEdit(e11); + Edit e12 = edit.new Edit(Action.INSERT_GAP, seqs, 5, 2, '-'); + e12.setSystemGenerated(true); + edit.addEdit(e12); + expected = MessageManager.formatMessage("label.delete_gaps", "3"); + assertEquals(SeqPanel.getEditStatusMessage(edit), expected); + } }