X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FSeqPanel.java;h=92a5030661f530abf504a32d908eb8bde230845e;hb=b87e96a7592f21c11e2e3a218b24de524c3a39fc;hp=0135e9d84096377b116e7a9125ec11c71def4006;hpb=793e6d46ae572461a030e2751c0d748f2521b71e;p=jalview.git 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); } /**