001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: * Chris.Dennis@invidi.com - http://bugs.eclipse.org/bugs/show_bug.cgi?id=29027
011: *******************************************************************************/package org.eclipse.ui.texteditor;
012:
013: import java.util.ResourceBundle;
014:
015: import org.eclipse.swt.custom.StyledText;
016:
017: import org.eclipse.jface.text.BadLocationException;
018: import org.eclipse.jface.text.IDocument;
019: import org.eclipse.jface.text.IRegion;
020: import org.eclipse.jface.text.Position;
021: import org.eclipse.jface.text.source.ISourceViewer;
022:
023: /**
024: * This action implements smart return.
025: * Instead of breaking the line where we are, we do the following:
026: * <p><b>Smart Enter</b>
027: * <ul>
028: * <li> if the caret is on a line containing any non-whitespace, a line is inserted below the
029: * current one and the caret moved to it,</li>
030: * <li> if the caret is on a whitespace-only line, a line is inserted below the current line,
031: * but the caret stays in its position.</li>
032: * </ul>
033: * </p>
034: * <p><b>Smart Enter Inverse</b>
035: * <ul>
036: * <li> if the caret is on a line containing any non-whitespace, we insert a line above the
037: * current one and move the caret to it (i.e. it stays at the same offset in the widget),</li>
038: * <li> if the caret is on a whitespace-only line, a line is inserted above the current line,
039: * but the caret stays in its logical position (i.e., it gets shifted one line down in the
040: * document, but keeps its position relative to the content following the caret).</li>
041: * </ul>
042: * </p>
043: * @since 3.0
044: */
045: public class InsertLineAction extends TextEditorAction {
046:
047: /**
048: * <code>true</code> if this action inserts a line above the current (Smart Enter Inverse),
049: * <code>false</code> otherwise
050: */
051: protected boolean fAbove;
052:
053: /**
054: * Creates a new smart enter action.
055: * @param bundle the resource bundle
056: * @param prefix the prefix to use to get properties from <code>bundle</code>
057: * @param textEditor the editor that the action acts upon
058: * @param above whether new lines are inserted above or below the caret's line.
059: */
060: public InsertLineAction(ResourceBundle bundle, String prefix,
061: ITextEditor textEditor, boolean above) {
062: super (bundle, prefix, textEditor);
063: fAbove = above;
064: }
065:
066: /*
067: * @see org.eclipse.ui.texteditor.TextEditorAction#update()
068: */
069: public void update() {
070: super .update();
071: if (isEnabled())
072: setEnabled(canModifyEditor());
073: }
074:
075: /*
076: * @see org.eclipse.jface.action.IAction#run()
077: */
078: public void run() {
079: /*
080: * Implementation note: instead of computing any indentations needed
081: * (which we can't at this generic level), we simply insert a new
082: * line delimiter either at the end of the current line (normal) or
083: * the end of the previous line (reverse). By operating directly on
084: * the text widget, any auto-indent strategies can pick up on the
085: * delimiter and perform any content-dependent modifications.
086: */
087:
088: ITextEditor ed = getTextEditor();
089: if (!(ed instanceof AbstractTextEditor))
090: return;
091:
092: if (!validateEditorInputState())
093: return;
094:
095: AbstractTextEditor editor = (AbstractTextEditor) ed;
096: ISourceViewer sv = editor.getSourceViewer();
097: if (sv == null)
098: return;
099:
100: IDocument document = sv.getDocument();
101: if (document == null)
102: return;
103:
104: StyledText st = sv.getTextWidget();
105: if (st == null || st.isDisposed())
106: return;
107:
108: try {
109: // get current line
110: int widgetOffset = st.getCaretOffset();
111: int offset = AbstractTextEditor.widgetOffset2ModelOffset(
112: sv, widgetOffset);
113: int currentLineNumber = document.getLineOfOffset(offset);
114: IRegion currentLine = document
115: .getLineInformation(currentLineNumber);
116:
117: int insertionOffset = -1;
118: if (fAbove) {
119: if (currentLineNumber != 0) {
120: IRegion previousLine = document
121: .getLineInformation(currentLineNumber - 1);
122: insertionOffset = previousLine.getOffset()
123: + previousLine.getLength();
124: }
125: } else {
126: insertionOffset = currentLine.getOffset()
127: + currentLine.getLength();
128: }
129:
130: boolean updateCaret = true;
131: int widgetInsertionOffset = AbstractTextEditor
132: .modelOffset2WidgetOffset(sv, insertionOffset);
133: if (widgetInsertionOffset == -1 && fAbove) {
134: // assume that the previous line was not accessible
135: // (e.g. folded, or we are on line 0)
136: // -> we insert the newline at the beginning of the current line, after any leading WS
137: insertionOffset = currentLine.getOffset()
138: + getIndentationLength(document, currentLine);
139: widgetInsertionOffset = AbstractTextEditor
140: .modelOffset2WidgetOffset(sv, insertionOffset);
141: updateCaret = false;
142: }
143: if (widgetInsertionOffset == -1)
144: return;
145:
146: // mark caret
147: Position caret = new Position(insertionOffset, 0);
148: document.addPosition(caret);
149: st.setSelectionRange(widgetInsertionOffset, 0);
150:
151: // operate directly on the widget
152: st.replaceTextRange(widgetInsertionOffset, 0, st
153: .getLineDelimiter());
154:
155: // restore caret unless an auto-indenter has already moved the caret
156: // then leave it alone
157: document.removePosition(caret);
158: if (updateCaret
159: && st.getSelection().x == widgetInsertionOffset) {
160: int widgetCaret = AbstractTextEditor
161: .modelOffset2WidgetOffset(sv, caret.getOffset());
162: if (widgetCaret != -1)
163: st.setSelectionRange(widgetCaret, 0);
164: st.showSelection();
165: }
166:
167: } catch (BadLocationException e) {
168: // ignore
169: }
170: }
171:
172: /**
173: * Computes the indentation length of a line.
174: *
175: * @param document the document
176: * @param line the line
177: * @return the number of whitespace characters at the beginning of
178: * <code>line</code>
179: * @throws BadLocationException on document access error
180: */
181: private int getIndentationLength(IDocument document, IRegion line)
182: throws BadLocationException {
183: int pos = line.getOffset();
184: int max = pos + line.getLength();
185: while (pos < max) {
186: if (!Character.isWhitespace(document.getChar(pos)))
187: break;
188: pos++;
189: }
190: return pos - line.getOffset();
191: }
192: }
|