### Eclipse Workspace Patch 1.0 #P org.eclipse.ui.workbench.texteditor Index: src/org/eclipse/ui/texteditor/AbstractTextEditor.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/AbstractTextEditor.java,v retrieving revision 1.293 diff -u -r1.293 AbstractTextEditor.java --- src/org/eclipse/ui/texteditor/AbstractTextEditor.java 27 Nov 2008 14:43:32 -0000 1.293 +++ src/org/eclipse/ui/texteditor/AbstractTextEditor.java 23 Dec 2008 14:39:49 -0000 @@ -12,6 +12,7 @@ * Genady Beryozkin, me@genady.org - https://bugs.eclipse.org/bugs/show_bug.cgi?id=11668 * Benjamin Muskalla - https://bugs.eclipse.org/bugs/show_bug.cgi?id=41573 * Stephan Wahlbrink - Wrong operations mode/feedback for text drag over/drop in text editors - https://bugs.eclipse.org/bugs/show_bug.cgi?id=206043 + * Tom Eicher (Avaloq Evolution AG) - block selection mode *******************************************************************************/ package org.eclipse.ui.texteditor; @@ -96,6 +97,7 @@ import org.eclipse.jface.preference.PreferenceConverter; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.util.Geometry; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.util.SafeRunnable; @@ -248,7 +250,7 @@ * {@link #COMMON_RULER_CONTEXT_MENU_ID}. *

*/ -public abstract class AbstractTextEditor extends EditorPart implements ITextEditor, IReusableEditor, ITextEditorExtension, ITextEditorExtension2, ITextEditorExtension3, ITextEditorExtension4, INavigationLocationProvider, ISaveablesSource, IPersistableEditor { +public abstract class AbstractTextEditor extends EditorPart implements ITextEditor, IReusableEditor, ITextEditorExtension, ITextEditorExtension2, ITextEditorExtension3, ITextEditorExtension4, ITextEditorExtension5, INavigationLocationProvider, ISaveablesSource, IPersistableEditor { /** * Tag used in xml configuration files to specify editor action contributions. @@ -286,6 +288,12 @@ private static final int SINGLE_CARET_WIDTH= 1; /** + * The symbolic name of the column mode font. + * @since 3.3 + */ + protected static final String COLUMN_MODE_FONT= "org.eclipse.ui.workbench.texteditor.columnModeFont"; //$NON-NLS-1$ + + /** * The text input listener. * * @see ITextInputListener @@ -568,7 +576,7 @@ class TextListener implements ITextListener, ITextInputListener { /** The posted updater code. */ - private Runnable fRunnable= new Runnable() { + private final Runnable fRunnable= new Runnable() { public void run() { fIsRunnablePosted= false; @@ -685,8 +693,16 @@ return; String property= event.getProperty(); + + boolean columnMode= isBlockSelectionEnabled(); - if (getFontPropertyPreferenceKey().equals(property)) { + if (columnMode) { + if (COLUMN_MODE_FONT.equals(property)) { + Font columnFont= JFaceResources.getFont(COLUMN_MODE_FONT); + disposeFont(); + setFont(fSourceViewer, columnFont); + } + } else if (getFontPropertyPreferenceKey().equals(property)) { initializeViewerFont(fSourceViewer); updateCaret(); } @@ -995,9 +1011,9 @@ protected static final class IdMapEntry { /** The action id. */ - private String fActionId; + private final String fActionId; /** The StyledText action. */ - private int fAction; + private final int fAction; /** * Creates a new mapping. @@ -1033,7 +1049,7 @@ class ScrollLinesAction extends Action { /** Number of lines to scroll. */ - private int fScrollIncrement; + private final int fScrollIncrement; /** * Creates a new scroll action that scroll the given number of lines. If the @@ -1120,7 +1136,7 @@ protected class LineEndAction extends TextNavigationAction { /** boolean flag which tells if the text up to the line end should be selected. */ - private boolean fDoSelect; + private final boolean fDoSelect; /** * Create a new line end action. @@ -1797,9 +1813,9 @@ */ private static final class InformationProvider implements IInformationProvider, IInformationProviderExtension, IInformationProviderExtension2 { - private IRegion fHoverRegion; - private Object fHoverInfo; - private IInformationControlCreator fControlCreator; + private final IRegion fHoverRegion; + private final Object fHoverInfo; + private final IInformationControlCreator fControlCreator; InformationProvider(IRegion hoverRegion, Object hoverInfo, IInformationControlCreator controlCreator) { fHoverRegion= hoverRegion; @@ -2021,6 +2037,7 @@ } } +// private Point fTextDNDColumnSelection; /** * Key used to look up font preference. @@ -2447,7 +2464,7 @@ * Ruler context menu listeners. * @since 2.0 */ - private List fRulerContextMenuListeners= new ArrayList(); + private final List fRulerContextMenuListeners= new ArrayList(); /** * Indicates whether sanity checking in enabled. * @since 2.0 @@ -2975,7 +2992,7 @@ if (fSelectionChangedListener == null) { fSelectionChangedListener= new ISelectionChangedListener() { - private Runnable fRunnable= new Runnable() { + private final Runnable fRunnable= new Runnable() { public void run() { // check whether editor has not been disposed yet if (fSourceViewer != null && fSourceViewer.getDocument() != null) { @@ -3462,11 +3479,9 @@ fTextDragAndDropToken= null; try { fSelection= st.getSelection(); - int offset= st.getOffsetAtLocation(new Point(event.x, event.y)); - Point p= st.getLocationAtOffset(offset); - if (p.x > event.x) - offset--; - event.doit= offset >= fSelection.x && offset < fSelection.y; +// if (st.getBlockSelection()) +// fTextDNDColumnSelection= st.getSelectionRange(); + event.doit= isLocationSelected(st, new Point(event.x, event.y)); ISelection selection= selectionProvider.getSelection(); if (selection instanceof ITextSelection) @@ -3477,6 +3492,14 @@ event.doit= false; } } + + private boolean isLocationSelected(StyledText widget, Point point) { + Point selection= widget.getSelection(); + Point upperLeft= widget.getLocationAtOffset(selection.x); + Point lowerRight= widget.getLocationAtOffset(selection.y); + lowerRight.y += widget.getLineHeight(selection.y); + return Geometry.createRectangle(upperLeft, Geometry.subtract(lowerRight, upperLeft)).contains(point); + } public void dragSetData(DragSourceEvent event) { event.data= fSelectedText; @@ -3564,16 +3587,37 @@ } String text= (String)event.data; - Point newSelection= st.getSelection(); - try { - int modelOffset= widgetOffset2ModelOffset(viewer, newSelection.x); - viewer.getDocument().replace(modelOffset, 0, text); - } catch (BadLocationException e) { - return; + if (isBlockSelectionEnabled()) { + // FIXME fix block selection and DND +// if (fTextDNDColumnSelection != null && fTextDragAndDropToken != null && event.detail == DND.DROP_MOVE) { +// // DND_MOVE within same editor - remove origin before inserting +// Rectangle newSelection= st.getColumnSelection(); +// st.replaceColumnSelection(fTextDNDColumnSelection, ""); //$NON-NLS-1$ +// st.replaceColumnSelection(newSelection, text); +// st.setColumnSelection(newSelection.x, newSelection.y, newSelection.x + fTextDNDColumnSelection.width - fTextDNDColumnSelection.x, newSelection.y + fTextDNDColumnSelection.height - fTextDNDColumnSelection.y); +// } else { +// Point newSelection= st.getSelection(); +// st.insert(text); +// IDocument document= getDocumentProvider().getDocument(getEditorInput()); +// int startLine= st.getLineAtOffset(newSelection.x); +// int startColumn= newSelection.x - st.getOffsetAtLine(startLine); +// int endLine= startLine + document.computeNumberOfLines(text); +// int endColumn= startColumn + TextUtilities.indexOf(document.getLegalLineDelimiters(), text, 0)[0]; +// st.setColumnSelection(startColumn, startLine, endColumn, endLine); +// } + } else { + Point newSelection= st.getSelection(); + try { + int modelOffset= widgetOffset2ModelOffset(viewer, newSelection.x); + viewer.getDocument().replace(modelOffset, 0, text); + } catch (BadLocationException e) { + return; + } + st.setSelectionRange(newSelection.x, text.length()); } - st.setSelectionRange(newSelection.x, text.length()); } finally { fTextDragAndDropToken= null; +// fTextDNDColumnSelection= null; } } }; @@ -3660,15 +3704,27 @@ if (font == null) font= JFaceResources.getTextFont(); - setFont(viewer, font); + if (!font.equals(fSourceViewer.getTextWidget().getFont())) { + setFont(viewer, font); + + disposeFont(); + if (!isSharedFont) + fFont= font; + } else if (!isSharedFont) { + font.dispose(); + } + } + /** + * Disposes of the non-shared font. + * + * @since 3.5 + */ + private void disposeFont() { if (fFont != null) { fFont.dispose(); fFont= null; } - - if (!isSharedFont) - fFont= font; } /** @@ -3681,7 +3737,8 @@ private void setFont(ISourceViewer sourceViewer, Font font) { if (sourceViewer.getDocument() != null) { - Point selection= sourceViewer.getSelectedRange(); + ISelectionProvider provider= sourceViewer.getSelectionProvider(); + ISelection selection= provider.getSelection(); int topIndex= sourceViewer.getTopIndex(); StyledText styledText= sourceViewer.getTextWidget(); @@ -3700,7 +3757,7 @@ e.setFont(font); } - sourceViewer.setSelectedRange(selection.x , selection.y); + provider.setSelection(selection); sourceViewer.setTopIndex(topIndex); if (parent instanceof Composite) { @@ -4160,10 +4217,7 @@ fTitleImage= null; } - if (fFont != null) { - fFont.dispose(); - fFont= null; - } + disposeFont(); disposeNonDefaultCaret(); fInitialCaret= null; @@ -4365,8 +4419,7 @@ } /** - * Returns the property preference key for the editor font. Subclasses may - * replace this method. + * Returns the property preference key for the editor font. * * @return a String with the key * @since 2.1 @@ -5707,6 +5760,10 @@ action.setActionDefinitionId(ITextEditorActionDefinitionIds.SHOW_INFORMATION); setAction(ITextEditorActionConstants.SHOW_INFORMATION, action); + action= new BlockModeToggleAction(EditorMessages.getBundleForConstructedKeys(), "Editor.ToggleColumnMode.", this); //$NON-NLS-1$ + action.setHelpContextId(IAbstractTextEditorHelpContextIds.COLUMN_MODE_ACTION); + action.setActionDefinitionId(ITextEditorActionDefinitionIds.BLOCK_MODE); + setAction(ITextEditorActionConstants.BLOCK_MODE, action); PropertyDialogAction openProperties= new PropertyDialogAction( new IShellProvider() { @@ -6893,7 +6950,7 @@ /** The cached editor. */ private ITextEditor fTextEditor; /** The cached editor input. */ - private IEditorInput fEditorInput; + private final IEditorInput fEditorInput; /** The cached document. */ private IDocument fDocument; @@ -7070,4 +7127,63 @@ } } + /* + * @see org.eclipse.ui.texteditor.ITextEditorExtension5#isColumnMode() + * @since 3.5 + */ + public final boolean isBlockSelectionEnabled() { + ISourceViewer viewer= getSourceViewer(); + if (viewer != null) { + StyledText styledText= viewer.getTextWidget(); + if (styledText != null) + return styledText.getBlockSelection(); + } + return false; + } + + /* + * @see org.eclipse.ui.texteditor.ITextEditorExtension5#setColumnMode(boolean) + * @since 3.5 + */ + public void setBlockSelectionMode(boolean enable) { + ISourceViewer viewer= getSourceViewer(); + if (viewer != null) { + StyledText styledText= viewer.getTextWidget(); + if (styledText != null) { + /* + * Font switching. Column mode needs a monospace font. We try to adapt the + * column font size to the current (normal) font size. + * - set the font _before enabling_ column mode in order to maintain the + * selection + * - revert the font _after disabling_ column mode in order to maintain the + * selection + */ + if (enable) { + Font columnFont= JFaceResources.getFont(COLUMN_MODE_FONT); + Font normalFont= styledText.getFont(); + if (!columnFont.equals(normalFont) && !normalFont.getFontData()[0].equals(columnFont.getFontData()[0])) { + int size= normalFont.getFontData()[0].getHeight(); + FontData[] fontData= columnFont.getFontData(); + boolean created= false; + if (fontData[0].getHeight() != size) { + for (int i= 0; i < fontData.length; i++) { + fontData[i].setHeight(size); + } + columnFont= new Font(columnFont.getDevice(), fontData); + created= true; + } + setFont(viewer, columnFont); + disposeFont(); + if (created) + fFont= columnFont; + } + } + + styledText.setBlockSelection(enable); + + if (!enable) + initializeViewerFont(viewer); + } + } + } } Index: src/org/eclipse/ui/texteditor/ITextEditorActionConstants.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ITextEditorActionConstants.java,v retrieving revision 1.44 diff -u -r1.44 ITextEditorActionConstants.java --- src/org/eclipse/ui/texteditor/ITextEditorActionConstants.java 24 Nov 2008 16:15:52 -0000 1.44 +++ src/org/eclipse/ui/texteditor/ITextEditorActionConstants.java 23 Dec 2008 14:39:50 -0000 @@ -9,6 +9,7 @@ * IBM Corporation - initial API and implementation * Genady Beryozkin, me@genady.org - https://bugs.eclipse.org/bugs/show_bug.cgi?id=11668 * Benjamin Muskalla - https://bugs.eclipse.org/bugs/show_bug.cgi?id=41573 + * Tom Eicher (Avaloq Evolution AG) - block selection mode *******************************************************************************/ package org.eclipse.ui.texteditor; @@ -664,4 +665,11 @@ * @since 3.3 */ String SHOW_INFORMATION= "ShowInformation"; //$NON-NLS-1$ + + /** + * Name of the action for toggling block selection mode. + * Value: "ColumnMode" + * @since 3.5 + */ + String BLOCK_MODE = "ColumnMode"; //$NON-NLS-1$ } Index: src/org/eclipse/ui/texteditor/CaseAction.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/CaseAction.java,v retrieving revision 1.12 diff -u -r1.12 CaseAction.java --- src/org/eclipse/ui/texteditor/CaseAction.java 16 Jul 2008 12:03:42 -0000 1.12 +++ src/org/eclipse/ui/texteditor/CaseAction.java 23 Dec 2008 14:39:49 -0000 @@ -8,16 +8,21 @@ * Contributors: * IBM Corporation - initial API and implementation * Sebastian Davids - bug 145326 [typing] toUpperCase incorrect selection + * Tom Eicher (Avaloq Evolution AG) - block selection mode *******************************************************************************/ package org.eclipse.ui.texteditor; import java.util.ResourceBundle; import org.eclipse.swt.custom.StyledText; -import org.eclipse.swt.graphics.Point; import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IBlockTextSelection; import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.text.ITextViewerExtension; +import org.eclipse.jface.text.JFaceTextUtil; import org.eclipse.jface.text.source.ISourceViewer; /** @@ -71,45 +76,38 @@ if (st == null) return; - Point sel= viewer.getSelectedRange(); - if (sel == null) - return; + ITextSelection selection= (ITextSelection) viewer.getSelectionProvider().getSelection(); + int adjustment= 0; try { - // if the selection is empty, we select the word / string using the viewer's - // double-click strategy - if (sel.y == 0) { - - // TODO find a better way to do this although there are multiple partitionings on a single document - -// String partition= getContentType(viewer, document, sel.x); -// SourceViewerConfiguration svc= fEditor.getSourceViewerConfiguration(); // never null when viewer instantiated -// ITextDoubleClickStrategy dcs= svc.getDoubleClickStrategy(viewer, partition); -// if (dcs != null) { -// dcs.doubleClicked(viewer); -// sel= viewer.getSelectedRange(); -// } - - if (sel.y == 0) - return; // if the selection is still empty, we're done + if (JFaceTextUtil.isEmpty(viewer, selection)) + return; + + IRegion[] ranges= JFaceTextUtil.getCoveredRanges(viewer, selection); + if (ranges.length > 1 && viewer instanceof ITextViewerExtension) + ((ITextViewerExtension) viewer).getRewriteTarget().beginCompoundChange(); + for (int i= 0; i < ranges.length; i++) { + IRegion region= ranges[i]; + String target= document.get(region.getOffset(), region.getLength()); + String replacement= (fToUpper ? target.toUpperCase() : target.toLowerCase()); + if (!target.equals(replacement)) { + document.replace(region.getOffset(), region.getLength(), replacement); + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=145326: replacement might be larger than the original + adjustment= replacement.length() - target.length(); + } } - - String target= document.get(sel.x, sel.y); - String replacement= (fToUpper ? target.toUpperCase() : target.toLowerCase()); - if (!target.equals(replacement)) { - document.replace(sel.x, target.length(), replacement); - // https://bugs.eclipse.org/bugs/show_bug.cgi?id=145326: replacement might be larger than the original - int adjustment= replacement.length() - target.length(); - if (adjustment > 0) - sel.y += adjustment; - } + if (ranges.length > 1 && viewer instanceof ITextViewerExtension) + ((ITextViewerExtension) viewer).getRewriteTarget().endCompoundChange(); } catch (BadLocationException x) { // ignore and return return; } // reinstall selection and move it into view - viewer.setSelectedRange(sel.x, sel.y); + if (!(selection instanceof IBlockTextSelection)) + viewer.setSelectedRange(selection.getOffset(), selection.getLength() + adjustment); + else + viewer.getSelectionProvider().setSelection(selection); // don't use the viewer's reveal feature in order to avoid jumping around st.showSelection(); } Index: src/org/eclipse/ui/texteditor/BasicTextEditorActionContributor.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/BasicTextEditorActionContributor.java,v retrieving revision 1.34 diff -u -r1.34 BasicTextEditorActionContributor.java --- src/org/eclipse/ui/texteditor/BasicTextEditorActionContributor.java 11 Sep 2008 11:58:02 -0000 1.34 +++ src/org/eclipse/ui/texteditor/BasicTextEditorActionContributor.java 23 Dec 2008 14:39:49 -0000 @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Tom Eicher (Avaloq Evolution AG) - block selection mode *******************************************************************************/ package org.eclipse.ui.texteditor; @@ -212,6 +213,7 @@ for (int i= 0; i < ACTIONS.length; i++) actionBars.setGlobalActionHandler(ACTIONS[i], getAction(editor, ACTIONS[i])); actionBars.setGlobalActionHandler(ITextEditorActionDefinitionIds.SHOW_WHITESPACE_CHARACTERS, getAction(editor, ITextEditorActionConstants.SHOW_WHITESPACE_CHARACTERS)); + actionBars.setGlobalActionHandler(ITextEditorActionDefinitionIds.BLOCK_MODE, getAction(editor, ITextEditorActionConstants.BLOCK_MODE)); fFindNext.setAction(getAction(editor, ITextEditorActionConstants.FIND_NEXT)); fFindPrevious.setAction(getAction(editor, ITextEditorActionConstants.FIND_PREVIOUS)); Index: src/org/eclipse/ui/texteditor/IAbstractTextEditorHelpContextIds.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/IAbstractTextEditorHelpContextIds.java,v retrieving revision 1.35 diff -u -r1.35 IAbstractTextEditorHelpContextIds.java --- src/org/eclipse/ui/texteditor/IAbstractTextEditorHelpContextIds.java 24 Nov 2008 16:15:52 -0000 1.35 +++ src/org/eclipse/ui/texteditor/IAbstractTextEditorHelpContextIds.java 23 Dec 2008 14:39:49 -0000 @@ -10,6 +10,7 @@ * Genady Beryozkin, me@genady.org - https://bugs.eclipse.org/bugs/show_bug.cgi?id=11668 * Benjamin Muskalla - https://bugs.eclipse.org/bugs/show_bug.cgi?id=41573 * Dakshinamurthy Karra (Jalian Systems) - Templates View - https://bugs.eclipse.org/bugs/show_bug.cgi?id=69581 + * Tom Eicher (Avaloq Evolution AG) - block selection mode *******************************************************************************/ package org.eclipse.ui.texteditor; @@ -387,4 +388,11 @@ * @since 3.4 */ String TEMPLATES_VIEW= PREFIX + "templates_view_context";//$NON-NLS-1$ + + /** + * Help context id for the column mode toggle action. + * Value: "column_mode_context_action_context" + * @since 3.5 + */ + String COLUMN_MODE_ACTION=PREFIX + "column_mode" + ACTION_POSTFIX; //$NON-NLS-1$ } Index: src/org/eclipse/ui/texteditor/DeleteLineAction.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/DeleteLineAction.java,v retrieving revision 1.16 diff -u -r1.16 DeleteLineAction.java --- src/org/eclipse/ui/texteditor/DeleteLineAction.java 22 Oct 2008 12:39:59 -0000 1.16 +++ src/org/eclipse/ui/texteditor/DeleteLineAction.java 23 Dec 2008 14:39:49 -0000 @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Tom Eicher (Avaloq Evolution AG) - block selection mode *******************************************************************************/ package org.eclipse.ui.texteditor; @@ -189,7 +190,10 @@ return; try { - fTarget.deleteLine(document, selection.getOffset(), selection.getLength(), fType, fCopyToClipboard); + if (fTarget instanceof TextViewerDeleteLineTarget) + ((TextViewerDeleteLineTarget) fTarget).deleteLine(document, selection, fType, fCopyToClipboard); + else + fTarget.deleteLine(document, selection.getOffset(), selection.getLength(), fType, fCopyToClipboard); } catch (BadLocationException e) { // should not happen } Index: src/org/eclipse/ui/texteditor/TextViewerDeleteLineTarget.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/TextViewerDeleteLineTarget.java,v retrieving revision 1.3 diff -u -r1.3 TextViewerDeleteLineTarget.java --- src/org/eclipse/ui/texteditor/TextViewerDeleteLineTarget.java 11 Sep 2008 11:58:02 -0000 1.3 +++ src/org/eclipse/ui/texteditor/TextViewerDeleteLineTarget.java 23 Dec 2008 14:39:50 -0000 @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Tom Eicher (Avaloq Evolution AG) - block selection mode *******************************************************************************/ package org.eclipse.ui.texteditor; @@ -34,10 +35,12 @@ import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextListener; +import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.ITextViewerExtension5; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.TextEvent; +import org.eclipse.jface.text.TextSelection; import org.eclipse.ui.internal.texteditor.TextEditorPlugin; @@ -255,32 +258,28 @@ } /** - * Returns the document's delete region specified by position and type. + * Returns the document's delete region according to selection and type. * * @param document the document - * @param offset the offset - * @param length the length + * @param selection the selection * @param type the line deletion type, must be one of WHOLE_LINE, * TO_BEGINNING or TO_END * @return the document's delete region * @throws BadLocationException if the document is accessed with invalid offset or line */ - private IRegion getDeleteRegion(IDocument document, int offset, int length, int type) throws BadLocationException { + private IRegion getDeleteRegion(IDocument document, ITextSelection selection, int type) throws BadLocationException { - int line= document.getLineOfOffset(offset); + int offset= selection.getOffset(); + int line= selection.getStartLine(); int resultOffset= 0; int resultLength= 0; switch (type) { - case DeleteLineAction.WHOLE: - resultOffset= document.getLineOffset(line); - int endOffset= offset + length; - IRegion endLineInfo= document.getLineInformationOfOffset(endOffset); - int endLine= document.getLineOfOffset(endLineInfo.getOffset()); - if (endLineInfo.getOffset() == endOffset && endLine > 0 && length > 0) - endLine= endLine - 1; - resultLength= document.getLineOffset(endLine) + document.getLineLength(endLine) - resultOffset; - break; + case DeleteLineAction.WHOLE: + resultOffset= document.getLineOffset(line); + int endLine= selection.getEndLine(); + resultLength= document.getLineOffset(endLine) + document.getLineLength(endLine) - resultOffset; + break; case DeleteLineAction.TO_BEGINNING: resultOffset= document.getLineOffset(line); @@ -338,8 +337,22 @@ * @since 3.4 */ public void deleteLine(IDocument document, int offset, int length, int type, boolean copyToClipboard) throws BadLocationException { + deleteLine(document, new TextSelection(offset, length), type, copyToClipboard); + } - IRegion deleteRegion= getDeleteRegion(document, offset, length, type); + /** + * Deletes the lines that intersect with the given selection. + * + * @param document the document + * @param selection the selection to use to determine the document range to delete + * @param type the line deletion type, must be one of + * WHOLE_LINE, TO_BEGINNING or TO_END + * @param copyToClipboard true if the deleted line should be copied to the clipboard + * @throws BadLocationException if position is not valid in the given document + * @since 3.5 + */ + public void deleteLine(IDocument document, ITextSelection selection, int type, boolean copyToClipboard) throws BadLocationException { + IRegion deleteRegion= getDeleteRegion(document, selection, type); int deleteOffset= deleteRegion.getOffset(); int deleteLength= deleteRegion.getLength(); Index: src/org/eclipse/ui/texteditor/MoveLinesAction.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/MoveLinesAction.java,v retrieving revision 1.21 diff -u -r1.21 MoveLinesAction.java --- src/org/eclipse/ui/texteditor/MoveLinesAction.java 11 Sep 2008 11:58:02 -0000 1.21 +++ src/org/eclipse/ui/texteditor/MoveLinesAction.java 23 Dec 2008 14:39:50 -0000 @@ -7,13 +7,13 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Tom Eicher (Avaloq Evolution AG) - block selection mode *******************************************************************************/ package org.eclipse.ui.texteditor; import java.util.ResourceBundle; import org.eclipse.swt.custom.StyledText; -import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Event; import org.eclipse.core.runtime.Assert; @@ -291,12 +291,10 @@ return; // get selection - Point p= fTextViewer.getSelectedRange(); - if (p == null) + ITextSelection sel= (ITextSelection) fTextViewer.getSelectionProvider().getSelection(); + if (sel.isEmpty()) return; - ITextSelection sel= new TextSelection(document, p.x, p.y); - ITextSelection skippedLine= getSkippedLine(document, sel); if (skippedLine == null) return; Index: src/org/eclipse/ui/texteditor/ConstructedEditorMessages.properties =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ConstructedEditorMessages.properties,v retrieving revision 1.13 diff -u -r1.13 ConstructedEditorMessages.properties --- src/org/eclipse/ui/texteditor/ConstructedEditorMessages.properties 24 Nov 2008 16:15:52 -0000 1.13 +++ src/org/eclipse/ui/texteditor/ConstructedEditorMessages.properties 23 Dec 2008 14:39:49 -0000 @@ -7,6 +7,7 @@ # # Contributors: # IBM Corporation - initial API and implementation +# Tom Eicher (Avaloq Evolution AG) - block selection mode ############################################################################### Editor.Undo.label=&Undo @@ -238,3 +239,8 @@ Editor.ShowInformation.tooltip= Shows the Tooltip Description Editor.ShowInformation.image= Editor.ShowInformation.description= Displays information for the current caret location in a sticky hover + +Editor.ToggleColumnMode.label= Toggle Column Mode +Editor.ToggleColumnMode.description= Enable rectangular / column selection in the current text editor +Editor.ToggleColumnMode.image= +Editor.ToggleColumnMode.tooltip= Toggle Column Selection Mode Index: src/org/eclipse/ui/texteditor/ITextEditorActionDefinitionIds.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/ITextEditorActionDefinitionIds.java,v retrieving revision 1.37 diff -u -r1.37 ITextEditorActionDefinitionIds.java --- src/org/eclipse/ui/texteditor/ITextEditorActionDefinitionIds.java 24 Nov 2008 10:28:59 -0000 1.37 +++ src/org/eclipse/ui/texteditor/ITextEditorActionDefinitionIds.java 23 Dec 2008 14:39:50 -0000 @@ -10,6 +10,7 @@ * Chris.Dennis@invidi.com - http://bugs.eclipse.org/bugs/show_bug.cgi?id=29027 * Genady Beryozkin, me@genady.org - https://bugs.eclipse.org/bugs/show_bug.cgi?id=11668 * Benjamin Muskalla - https://bugs.eclipse.org/bugs/show_bug.cgi?id=41573 + * Tom Eicher (Avaloq Evolution AG) - block selection mode *******************************************************************************/ package org.eclipse.ui.texteditor; @@ -569,4 +570,10 @@ */ String SHOW_INFORMATION= "org.eclipse.ui.edit.text.showInformation"; //$NON-NLS-1$ + /** + * Command ID of the command to toggle block selection mode. + * Value: "org.eclipse.ui.edit.text.toggleColumnMode"). + * @since 3.5 + */ + String BLOCK_MODE = "org.eclipse.ui.edit.text.toggleColumnMode"; //$NON-NLS-1$ } Index: plugin.properties =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.ui.workbench.texteditor/plugin.properties,v retrieving revision 1.47 diff -u -r1.47 plugin.properties --- plugin.properties 15 Mar 2007 10:25:00 -0000 1.47 +++ plugin.properties 23 Dec 2008 14:39:44 -0000 @@ -7,6 +7,7 @@ # # Contributors: # IBM Corporation - initial API and implementation +# Tom Eicher (Avaloq Evolution AG) - block selection mode ############################################################################### pluginName= Text Editor Framework providerName= Eclipse.org @@ -32,6 +33,10 @@ showInformation.name= Show Tooltip Description showInformation.description= Displays information for the current caret location in a sticky hover +toggleColumnMode.label= Enable Block Selection +toggleColumnMode.tooltip= Enables block selection mode +toggleColumnMode.description= Enable block / column selection in the current text editor + smartEnter.label= Insert Line Below Current Line smartEnter.description= Adds a new line below the current line smartEnterInverse.label= Insert Line Above Current Line @@ -169,3 +174,5 @@ SpellingEngine= Spelling Engine +columnModeFont.label= Text Editor Block Selection Font +columnModeFont.description= The block selection mode font is used by text editors in block (column) mode. A monospace font should be used. Index: plugin.xml =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.ui.workbench.texteditor/plugin.xml,v retrieving revision 1.91 diff -u -r1.91 plugin.xml --- plugin.xml 2 May 2007 20:23:35 -0000 1.91 +++ plugin.xml 23 Dec 2008 14:39:45 -0000 @@ -399,6 +399,12 @@ categoryId="org.eclipse.ui.category.window" id="org.eclipse.ui.edit.text.toggleShowSelectedElementOnly"> + + + + + + + + + %columnModeFont.description + + + + + + + + + Index: src/org/eclipse/ui/texteditor/ITextEditorExtension5.java =================================================================== RCS file: src/org/eclipse/ui/texteditor/ITextEditorExtension5.java diff -N src/org/eclipse/ui/texteditor/ITextEditorExtension5.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/ui/texteditor/ITextEditorExtension5.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2007 Avaloq Evolution AG and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tom Eicher (Avaloq Evolution AG) - initial API and implementation + *******************************************************************************/ + +package org.eclipse.ui.texteditor; + + +/** + * Extension interface for {@link org.eclipse.ui.texteditor.ITextEditor}. Adds the following + * functions: + *
    + *
  • block selection mode
  • + *
+ *

+ * This interface may be implemented by clients. + *

+ * + * @since 3.5 + */ +public interface ITextEditorExtension5 { + /** + * Returns true if the receiver is in block (aka rectangular or column) selection + * mode, false otherwise. + * + * @return the receiver's block selection state + */ + boolean isBlockSelectionEnabled(); + + /** + * Sets the block selection mode state of the receiver to enable. Nothing happens + * if the receiver already is in the requested state. + * + * @param enable the new block selection state + */ + void setBlockSelectionMode(boolean enable); +} Index: src/org/eclipse/ui/texteditor/BlockModeToggleAction.java =================================================================== RCS file: src/org/eclipse/ui/texteditor/BlockModeToggleAction.java diff -N src/org/eclipse/ui/texteditor/BlockModeToggleAction.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/ui/texteditor/BlockModeToggleAction.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2007 Avaloq Evolution AG and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tom Eicher (Avaloq Evolution AG) - initial API and implementation + *******************************************************************************/ +package org.eclipse.ui.texteditor; + +import java.util.ResourceBundle; + +import org.eclipse.jface.action.IAction; + + +/** + * This action toggles the block (aka rectangular) selection mode. + *

+ * This API is provisional and may change any time before the 3.5 API freeze. + *

+ * + * @since 3.5 + */ +final class BlockModeToggleAction extends TextEditorAction { + + /** + * Construct the action and initialize its state. + * + * @param resourceBundle the resource bundle to construct label and tooltip from + * @param prefix the prefix to use for constructing resource bundle keys + * @param editor the editor this action is associated with + */ + public BlockModeToggleAction(ResourceBundle resourceBundle, String prefix, ITextEditor editor) { + super(resourceBundle, prefix, editor, IAction.AS_CHECK_BOX); + } + + /* + * @see org.eclipse.jface.action.Action#run() + */ + public void run() { + ITextEditor editor= getTextEditor(); + if (editor instanceof ITextEditorExtension5) { + ITextEditorExtension5 ext5= (ITextEditorExtension5) editor; + ext5.setBlockSelectionMode(!ext5.isBlockSelectionEnabled()); + } + update(); // update in case anyone else has directly accessed the widget + } + + /* + * @see org.eclipse.ui.texteditor.TextEditorAction#update() + */ + public void update() { + ITextEditor editor= getTextEditor(); + if (editor instanceof ITextEditorExtension5) { + ITextEditorExtension5 ext5= (ITextEditorExtension5) editor; + setEnabled(true); + setChecked(ext5.isBlockSelectionEnabled()); + return; + } + setEnabled(false); + setChecked(false); + } +} #P org.eclipse.jdt.ui Index: ui/org/eclipse/jdt/ui/text/JavaSourceViewerConfiguration.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/text/JavaSourceViewerConfiguration.java,v retrieving revision 1.159 diff -u -r1.159 JavaSourceViewerConfiguration.java --- ui/org/eclipse/jdt/ui/text/JavaSourceViewerConfiguration.java 15 Sep 2008 14:16:47 -0000 1.159 +++ ui/org/eclipse/jdt/ui/text/JavaSourceViewerConfiguration.java 23 Dec 2008 14:39:56 -0000 @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Tom Eicher (Avaloq Evolution AG) - block selection mode *******************************************************************************/ package org.eclipse.jdt.ui.text; @@ -459,9 +460,9 @@ else if (IJavaPartitions.JAVA_STRING.equals(contentType)) return new IAutoEditStrategy[] { new SmartSemicolonAutoEditStrategy(partitioning), new JavaStringAutoIndentStrategy(partitioning) }; else if (IJavaPartitions.JAVA_CHARACTER.equals(contentType) || IDocument.DEFAULT_CONTENT_TYPE.equals(contentType)) - return new IAutoEditStrategy[] { new SmartSemicolonAutoEditStrategy(partitioning), new JavaAutoIndentStrategy(partitioning, getProject()) }; + return new IAutoEditStrategy[] { new SmartSemicolonAutoEditStrategy(partitioning), new JavaAutoIndentStrategy(partitioning, getProject(), sourceViewer) }; else - return new IAutoEditStrategy[] { new JavaAutoIndentStrategy(partitioning, getProject()) }; + return new IAutoEditStrategy[] { new JavaAutoIndentStrategy(partitioning, getProject(), sourceViewer) }; } /* Index: ui/org/eclipse/jdt/internal/ui/javaeditor/JavaMoveLinesAction.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/javaeditor/JavaMoveLinesAction.java,v retrieving revision 1.12 diff -u -r1.12 JavaMoveLinesAction.java --- ui/org/eclipse/jdt/internal/ui/javaeditor/JavaMoveLinesAction.java 11 Sep 2008 11:59:24 -0000 1.12 +++ ui/org/eclipse/jdt/internal/ui/javaeditor/JavaMoveLinesAction.java 23 Dec 2008 14:39:55 -0000 @@ -7,13 +7,13 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Tom Eicher (Avaloq Evolution AG) - block selection mode *******************************************************************************/ package org.eclipse.jdt.internal.ui.javaeditor; import java.util.ResourceBundle; import org.eclipse.swt.custom.StyledText; -import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Event; import org.eclipse.core.runtime.Assert; @@ -320,12 +320,10 @@ return; // get selection - Point p= viewer.getSelectedRange(); - if (p == null) + ITextSelection sel= (ITextSelection) viewer.getSelectionProvider().getSelection(); + if (sel.isEmpty()) return; - ITextSelection sel= new TextSelection(document, p.x, p.y); - ITextSelection skippedLine= getSkippedLine(document, sel); if (skippedLine == null) return; @@ -436,7 +434,14 @@ if (numberOfLines < 1) return new Region(offset, 0); int endLine= startLine + numberOfLines - 1; - int endOffset= document.getLineOffset(endLine) + document.getLineLength(endLine); + int endOffset; + if (fSharedState.fEditor.isBlockSelectionEnabled()) { + // in column mode, don't select the last delimiter as we count an empty selected line + IRegion endLineInfo= document.getLineInformation(endLine); + endOffset= endLineInfo.getOffset() + endLineInfo.getLength(); + } else { + endOffset= document.getLineOffset(endLine) + document.getLineLength(endLine); + } return new Region(offset, endOffset - offset); } Index: ui/org/eclipse/jdt/internal/ui/javaeditor/JavaEditor.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/javaeditor/JavaEditor.java,v retrieving revision 1.471 diff -u -r1.471 JavaEditor.java --- ui/org/eclipse/jdt/internal/ui/javaeditor/JavaEditor.java 17 Nov 2008 16:49:35 -0000 1.471 +++ ui/org/eclipse/jdt/internal/ui/javaeditor/JavaEditor.java 23 Dec 2008 14:39:54 -0000 @@ -8,6 +8,7 @@ * Contributors: * IBM Corporation - initial API and implementation * Tom Eicher - [formatting] 'Format Element' in JavaDoc does also format method body - https://bugs.eclipse.org/bugs/show_bug.cgi?id=238746 + * Tom Eicher (Avaloq Evolution AG) - block selection mode *******************************************************************************/ package org.eclipse.jdt.internal.ui.javaeditor; @@ -761,12 +762,17 @@ return; int next= findNextPosition(position); - if (next != BreakIterator.DONE) { - setCaretPosition(next); - getTextWidget().showSelection(); - fireSelectionChanged(); + try { + if (isBlockSelectionEnabled() && document.getLineOfOffset(next) != document.getLineOfOffset(position)) { + super.run(); // may navigate into virtual white space + } else if (next != BreakIterator.DONE) { + setCaretPosition(next); + getTextWidget().showSelection(); + fireSelectionChanged(); + } + } catch (BadLocationException x) { + // ignore } - } /** @@ -858,20 +864,33 @@ return; final ISourceViewer viewer= getSourceViewer(); - final int caret, length; - Point selection= viewer.getSelectedRange(); - if (selection.y != 0) { - caret= selection.x; - length= selection.y; + StyledText text= viewer.getTextWidget(); + Point widgetSelection= text.getSelection(); + if (isBlockSelectionEnabled() && widgetSelection.y != widgetSelection.x) { + final int caret= text.getCaretOffset(); + final int offset= modelOffset2WidgetOffset(viewer, position); + + if (caret == widgetSelection.x) + text.setSelectionRange(widgetSelection.y, offset - widgetSelection.y); + else + text.setSelectionRange(widgetSelection.x, offset - widgetSelection.x); + text.invokeAction(ST.DELETE_NEXT); } else { - caret= widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset()); - length= position - caret; - } + Point selection= viewer.getSelectedRange(); + final int caret, length; + if (selection.y != 0) { + caret= selection.x; + length= selection.y; + } else { + caret= widgetOffset2ModelOffset(viewer, text.getCaretOffset()); + length= position - caret; + } - try { - viewer.getDocument().replace(caret, length, ""); //$NON-NLS-1$ - } catch (BadLocationException exception) { - // Should not happen + try { + viewer.getDocument().replace(caret, length, ""); //$NON-NLS-1$ + } catch (BadLocationException exception) { + // Should not happen + } } } @@ -955,10 +974,16 @@ return; int previous= findPreviousPosition(position); - if (previous != BreakIterator.DONE) { - setCaretPosition(previous); - getTextWidget().showSelection(); - fireSelectionChanged(); + try { + if (isBlockSelectionEnabled() && document.getLineOfOffset(previous) != document.getLineOfOffset(position)) { + super.run(); // may navigate into virtual white space + } else if (previous != BreakIterator.DONE) { + setCaretPosition(previous); + getTextWidget().showSelection(); + fireSelectionChanged(); + } + } catch (BadLocationException x) { + // ignore - getLineOfOffset failed } } @@ -1053,18 +1078,31 @@ final int length; final ISourceViewer viewer= getSourceViewer(); - Point selection= viewer.getSelectedRange(); - if (selection.y != 0) { - position= selection.x; - length= selection.y; + StyledText text= viewer.getTextWidget(); + Point widgetSelection= text.getSelection(); + if (isBlockSelectionEnabled() && widgetSelection.y != widgetSelection.x) { + final int caret= text.getCaretOffset(); + final int offset= modelOffset2WidgetOffset(viewer, position); + + if (caret == widgetSelection.x) + text.setSelectionRange(widgetSelection.y, offset - widgetSelection.y); + else + text.setSelectionRange(widgetSelection.x, offset - widgetSelection.x); + text.invokeAction(ST.DELETE_PREVIOUS); } else { - length= widgetOffset2ModelOffset(viewer, viewer.getTextWidget().getCaretOffset()) - position; - } + Point selection= viewer.getSelectedRange(); + if (selection.y != 0) { + position= selection.x; + length= selection.y; + } else { + length= widgetOffset2ModelOffset(viewer, text.getCaretOffset()) - position; + } - try { - viewer.getDocument().replace(position, length, ""); //$NON-NLS-1$ - } catch (BadLocationException exception) { - // Should not happen + try { + viewer.getDocument().replace(position, length, ""); //$NON-NLS-1$ + } catch (BadLocationException exception) { + // Should not happen + } } } Index: ui/org/eclipse/jdt/internal/ui/actions/IndentAction.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/actions/IndentAction.java,v retrieving revision 1.48 diff -u -r1.48 IndentAction.java --- ui/org/eclipse/jdt/internal/ui/actions/IndentAction.java 11 Sep 2008 11:59:06 -0000 1.48 +++ ui/org/eclipse/jdt/internal/ui/actions/IndentAction.java 23 Dec 2008 14:39:52 -0000 @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Tom Eicher (Avaloq Evolution AG) - block selection mode *******************************************************************************/ package org.eclipse.jdt.internal.ui.actions; @@ -148,10 +149,8 @@ try { document.addPosition(end); - firstLine= document.getLineOfOffset(offset); - // check for marginal (zero-length) lines - int minusOne= length == 0 ? 0 : 1; - nLines= document.getLineOfOffset(offset + length - minusOne) - firstLine + 1; + firstLine= selection.getStartLine(); + nLines= selection.getEndLine() - firstLine + 1; } catch (BadLocationException e) { // will only happen on concurrent modification JavaPlugin.log(new Status(IStatus.ERROR, JavaPlugin.getPluginId(), IStatus.OK, "", e)); //$NON-NLS-1$ Index: ui/org/eclipse/jdt/internal/ui/text/java/JavaAutoIndentStrategy.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/java/JavaAutoIndentStrategy.java,v retrieving revision 1.115 diff -u -r1.115 JavaAutoIndentStrategy.java --- ui/org/eclipse/jdt/internal/ui/text/java/JavaAutoIndentStrategy.java 24 Nov 2008 12:55:16 -0000 1.115 +++ ui/org/eclipse/jdt/internal/ui/text/java/JavaAutoIndentStrategy.java 23 Dec 2008 14:39:55 -0000 @@ -8,6 +8,7 @@ * Contributors: * IBM Corporation - initial API and implementation * Nikolay Metchev - Fixed https://bugs.eclipse.org/bugs/show_bug.cgi?id=29909 + * Tom Eicher (Avaloq Evolution AG) - block selection mode *******************************************************************************/ package org.eclipse.jdt.internal.ui.text.java; @@ -27,6 +28,7 @@ import org.eclipse.jface.text.Region; import org.eclipse.jface.text.TextUtilities; import org.eclipse.jface.text.rules.FastPartitioner; +import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IWorkbenchPage; @@ -92,16 +94,23 @@ private String fPartitioning; private final IJavaProject fProject; private static IScanner fgScanner= ToolFactory.createScanner(false, false, false, false); + /** + * The viewer. + * @since 3.5 + */ + private final ISourceViewer fViewer; /** * Creates a new Java auto indent strategy for the given document partitioning. * * @param partitioning the document partitioning * @param project the project to get formatting preferences from, or null to use default preferences + * @param viewer the source viewer that this strategy is attached to */ - public JavaAutoIndentStrategy(String partitioning, IJavaProject project) { + public JavaAutoIndentStrategy(String partitioning, IJavaProject project, ISourceViewer viewer) { fPartitioning= partitioning; fProject= project; + fViewer= viewer; } private int getBracketCount(IDocument d, int startOffset, int endOffset, boolean ignoreCloseBrackets) throws BadLocationException { @@ -1211,7 +1220,8 @@ else if (c.text.length() == 1) smartIndentOnKeypress(d, c); else if (c.text.length() > 1 && getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_SMART_PASTE)) - smartPaste(d, c); // no smart backspace for paste + if (fViewer == null || fViewer.getTextWidget() == null || !fViewer.getTextWidget().getBlockSelection()) + smartPaste(d, c); // no smart backspace for paste } #P org.eclipse.jface.text Index: src/org/eclipse/jface/text/source/SourceViewer.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jface.text/src/org/eclipse/jface/text/source/SourceViewer.java,v retrieving revision 1.84 diff -u -r1.84 SourceViewer.java --- src/org/eclipse/jface/text/source/SourceViewer.java 11 Sep 2008 11:58:24 -0000 1.84 +++ src/org/eclipse/jface/text/source/SourceViewer.java 23 Dec 2008 14:40:02 -0000 @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Tom Eicher (Avaloq Evolution AG) - block selection mode *******************************************************************************/ package org.eclipse.jface.text.source; @@ -27,8 +28,10 @@ import org.eclipse.jface.text.AbstractHoverInformationControlManager; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.BadPositionCategoryException; +import org.eclipse.jface.text.BlockTextSelection; import org.eclipse.jface.text.DocumentRewriteSession; import org.eclipse.jface.text.DocumentRewriteSessionType; +import org.eclipse.jface.text.IBlockTextSelection; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentExtension4; import org.eclipse.jface.text.IPositionUpdater; @@ -36,6 +39,7 @@ import org.eclipse.jface.text.IRewriteTarget; import org.eclipse.jface.text.ISlaveDocumentManager; import org.eclipse.jface.text.ISlaveDocumentManagerExtension; +import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.ITextViewerExtension2; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.Region; @@ -715,6 +719,20 @@ } /** + * Position storing block selection information in order to maintain a column selection. + * + * @since 3.5 + */ + private static final class ColumnPosition extends Position { + int fStartColumn, fEndColumn; + ColumnPosition(int offset, int length, int startColumn, int endColumn) { + super(offset, length); + fStartColumn= startColumn; + fEndColumn= endColumn; + } + } + + /** * Remembers and returns the current selection. The saved selection can be restored * by calling restoreSelection(). * @@ -724,7 +742,7 @@ */ protected Point rememberSelection() { - final Point selection= getSelectedRange(); + final ITextSelection selection= (ITextSelection) getSelection(); final IDocument document= getDocument(); if (fSelections.isEmpty()) { @@ -735,8 +753,11 @@ } try { - - final Position position= new Position(selection.x, selection.y); + final Position position; + if (selection instanceof IBlockTextSelection) + position= new ColumnPosition(selection.getOffset(), selection.getLength(), ((IBlockTextSelection) selection).getStartColumn(), ((IBlockTextSelection) selection).getEndColumn()); + else + position= new Position(selection.getOffset(), selection.getLength()); document.addPosition(fSelectionCategory, position); fSelections.push(position); @@ -746,7 +767,7 @@ // Should not happen } - return selection; + return new Point(selection.getOffset(), selection.getLength()); } /** @@ -766,13 +787,20 @@ try { document.removePosition(fSelectionCategory, position); Point currentSelection= getSelectedRange(); - if (currentSelection == null || currentSelection.x != position.getOffset() || currentSelection.y != position.getLength()) - setSelectedRange(position.getOffset(), position.getLength()); + if (currentSelection == null || currentSelection.x != position.getOffset() || currentSelection.y != position.getLength()) { + if (position instanceof ColumnPosition && getTextWidget().getBlockSelection()) { + setSelection(new BlockTextSelection(document, document.getLineOfOffset(position.getOffset()), ((ColumnPosition) position).fStartColumn, document.getLineOfOffset(position.getOffset() + position.getLength()), ((ColumnPosition) position).fEndColumn, getTextWidget().getTabs())); + } else { + setSelectedRange(position.getOffset(), position.getLength()); + } + } if (fSelections.isEmpty()) clearRememberedSelection(); } catch (BadPositionCategoryException exception) { // Should not happen + } catch (BadLocationException x) { + // Should not happen } } } Index: src/org/eclipse/jface/text/TextViewer.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jface.text/src/org/eclipse/jface/text/TextViewer.java,v retrieving revision 1.204 diff -u -r1.204 TextViewer.java --- src/org/eclipse/jface/text/TextViewer.java 19 Dec 2008 09:45:36 -0000 1.204 +++ src/org/eclipse/jface/text/TextViewer.java 23 Dec 2008 14:40:01 -0000 @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Tom Eicher (Avaloq Evolution AG) - block selection mode *******************************************************************************/ package org.eclipse.jface.text; @@ -2415,6 +2416,16 @@ * @see Viewer#setSelection(ISelection) */ public void setSelection(ISelection selection, boolean reveal) { +// // FIXME ViewerState should also support column selections +// if (selection instanceof IColumnTextSelection && getTextWidget().getBlockSelection()) { +// IColumnTextSelection s= (IColumnTextSelection) selection; +// StyledText styledText= getTextWidget(); +// int startLine= modelLine2WidgetLine(s.getStartLine()); +// int endLine= modelLine2WidgetLine(s.getEndLine()); +// styledText.setBlColumnSelection(s.getStartColumn(), startLine, s.getEndColumn(), endLine); +// if (reveal) +// revealRange(s.getOffset(), s.getLength()); +// } else if (selection instanceof ITextSelection) { if (selection instanceof ITextSelection) { ITextSelection s= (ITextSelection) selection; setSelectedRange(s.getOffset(), s.getLength()); @@ -2427,6 +2438,28 @@ * @see Viewer#getSelection() */ public ISelection getSelection() { + if (redraws() && fTextWidget != null && fTextWidget.getBlockSelection()) { + int[] ranges= fTextWidget.getSelectionRanges(); + int startOffset= ranges[0]; + int endOffset= ranges[ranges.length - 2] + ranges[ranges.length - 1]; + + IDocument document= getDocument(); + startOffset= widgetOffset2ModelOffset(startOffset); + endOffset= widgetOffset2ModelOffset(endOffset); + try { + int startLine= document.getLineOfOffset(startOffset); + int endLine= document.getLineOfOffset(endOffset); + + int startColumn= startOffset - document.getLineOffset(startLine); + int endColumn= endOffset - document.getLineOffset(endLine); + if (startLine == -1 || endLine == -1) + return TextSelection.emptySelection(); + return new BlockTextSelection(document, startLine, startColumn, endLine, endColumn, fTextWidget.getTabs()); + } catch (BadLocationException e) { + return TextSelection.emptySelection(); + } + } + Point p= getSelectedRange(); if (p.x == -1 || p.y == -1) return TextSelection.emptySelection(); @@ -3965,13 +3998,15 @@ * @return the region describing the text block comprising the given selection * @since 2.0 */ - private IRegion getTextBlockFromSelection(Point selection) { + private IRegion getTextBlockFromSelection(ITextSelection selection) { try { IDocument document= getDocument(); - IRegion line= document.getLineInformationOfOffset(selection.x); - int length= selection.y == 0 ? line.getLength() : selection.y + (selection.x - line.getOffset()); - return new Region(line.getOffset(), length); + int start= document.getLineOffset(selection.getStartLine()); + int endLine= selection.getEndLine(); + IRegion endLineInfo= document.getLineInformation(endLine); + int end= endLineInfo.getOffset() + endLineInfo.getLength(); + return new Region(start, end - start); } catch (BadLocationException x) { } @@ -4010,7 +4045,7 @@ Map partitioners= null; DocumentRewriteSession rewriteSession= null; try { - Point selection= getSelectedRange(); + ITextSelection selection= (ITextSelection) getSelection(); IRegion block= getTextBlockFromSelection(selection); ITypedRegion[] regions= TextUtilities.computePartitioning(d, getDocumentPartitioning(), block.getOffset(), block.getLength(), false); Index: src/org/eclipse/jface/text/TextSelection.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jface.text/src/org/eclipse/jface/text/TextSelection.java,v retrieving revision 1.13 diff -u -r1.13 TextSelection.java --- src/org/eclipse/jface/text/TextSelection.java 12 Sep 2008 14:44:18 -0000 1.13 +++ src/org/eclipse/jface/text/TextSelection.java 23 Dec 2008 14:39:59 -0000 @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Tom Eicher (Avaloq Evolution AG) - block selection mode *******************************************************************************/ package org.eclipse.jface.text; @@ -17,7 +18,7 @@ /** * Standard implementation of {@link org.eclipse.jface.text.ITextSelection}. *

- * Makes advantage of the weak contract of correctness of its interface. If + * Takes advantage of the weak contract of correctness of its interface. If * generated from a selection provider, it only remembers its offset and length * and computes the remaining information on request.

*/ @@ -42,8 +43,8 @@ return NULL; } - /** Document which delivers the data of the selection */ - private IDocument fDocument; + /** Document which delivers the data of the selection, possibly null. */ + protected final IDocument fDocument; /** Offset of the selection */ private int fOffset; /** Length of the selection */ @@ -56,6 +57,7 @@ private TextSelection() { fOffset= -1; fLength= -1; + fDocument= null; } /** Index: src/org/eclipse/jface/text/CursorLinePainter.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jface.text/src/org/eclipse/jface/text/CursorLinePainter.java,v retrieving revision 1.21 diff -u -r1.21 CursorLinePainter.java --- src/org/eclipse/jface/text/CursorLinePainter.java 11 Sep 2008 11:58:23 -0000 1.21 +++ src/org/eclipse/jface/text/CursorLinePainter.java 23 Dec 2008 14:39:59 -0000 @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Tom Eicher (Avaloq Evolution AG) - block selection mode *******************************************************************************/ package org.eclipse.jface.text; @@ -77,7 +78,7 @@ int caret= textWidget.getCaretOffset(); int length= event.lineText.length(); - if (event.lineOffset <= caret && caret <= event.lineOffset + length) + if (event.lineOffset <= caret && caret <= event.lineOffset + length && !hasMultiLineSelection(textWidget)) event.lineBackground= fHighlightColor; else event.lineBackground= textWidget.getBackground(); @@ -223,10 +224,7 @@ StyledText textWidget= fViewer.getTextWidget(); // check selection - Point selection= textWidget.getSelection(); - int startLine= textWidget.getLineAtOffset(selection.x); - int endLine= textWidget.getLineAtOffset(selection.y); - if (startLine != endLine) { + if (hasMultiLineSelection(textWidget)) { deactivate(true); return; } @@ -247,6 +245,22 @@ } } + /** + * Returns true if the widget has a selection spanning multiple lines, + * false otherwise. + * + * @param textWidget the text widget to check + * @return true if textWidget has a multiline selection, + * false otherwise + * @since 3.5 + */ + private boolean hasMultiLineSelection(StyledText textWidget) { + Point selection= textWidget.getSelection(); + int startLine= textWidget.getLineAtOffset(selection.x); + int endLine= textWidget.getLineAtOffset(selection.y); + return startLine != endLine; + } + /* * @see IPainter#setPositionManager(IPaintPositionManager) */ Index: src/org/eclipse/jface/text/WhitespaceCharacterPainter.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jface.text/src/org/eclipse/jface/text/WhitespaceCharacterPainter.java,v retrieving revision 1.11 diff -u -r1.11 WhitespaceCharacterPainter.java --- src/org/eclipse/jface/text/WhitespaceCharacterPainter.java 11 Sep 2008 11:58:23 -0000 1.11 +++ src/org/eclipse/jface/text/WhitespaceCharacterPainter.java 23 Dec 2008 14:40:01 -0000 @@ -9,6 +9,7 @@ * Anton Leherbauer (Wind River Systems) - initial API and implementation - https://bugs.eclipse.org/bugs/show_bug.cgi?id=22712 * Anton Leherbauer (Wind River Systems) - [painting] Long lines take too long to display when "Show Whitespace Characters" is enabled - https://bugs.eclipse.org/bugs/show_bug.cgi?id=196116 * Anton Leherbauer (Wind River Systems) - [painting] Whitespace characters not drawn when scrolling to right slowly - https://bugs.eclipse.org/bugs/show_bug.cgi?id=206633 + * Tom Eicher (Avaloq Evolution AG) - block selection mode *******************************************************************************/ package org.eclipse.jface.text; @@ -229,7 +230,6 @@ String text= content.getTextRange(startOffset, length); StyleRange styleRange= null; Color fg= null; - Point selection= fTextWidget.getSelection(); StringBuffer visibleChar= new StringBuffer(10); for (int textOffset= 0; textOffset <= length; ++textOffset) { int delta= 0; @@ -270,7 +270,7 @@ if (visibleChar.length() > 0) { int widgetOffset= startOffset + textOffset - visibleChar.length() + delta; if (!eol || !isFoldedLine(content.getLineAtOffset(widgetOffset))) { - if (widgetOffset >= selection.x && widgetOffset < selection.y) { + if (isOffsetSelected(fTextWidget, widgetOffset)) { fg= fTextWidget.getSelectionForeground(); } else if (styleRange == null || styleRange.start + styleRange.length <= widgetOffset) { styleRange= fTextWidget.getStyleRangeAtOffset(widgetOffset); @@ -286,6 +286,23 @@ } } } + + /** + * Returns true if offset is selection in widget, + * false otherwise. + * + * @param widget the widget + * @param offset the offset + * @return true if offset is selection, false otherwise + * @since 3.5 + */ + private static final boolean isOffsetSelected(StyledText widget, int offset) { + int[] ranges= widget.getSelectionRanges(); + for (int i= 0; i < ranges.length; i+= 2) + if (ranges[i] <= offset && offset <= ranges[i + 1]) + return true; + return false; + } /** * Check if the given widget line is a folded line. Index: src/org/eclipse/jface/text/JFaceTextUtil.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jface.text/src/org/eclipse/jface/text/JFaceTextUtil.java,v retrieving revision 1.8 diff -u -r1.8 JFaceTextUtil.java --- src/org/eclipse/jface/text/JFaceTextUtil.java 27 Nov 2008 14:43:08 -0000 1.8 +++ src/org/eclipse/jface/text/JFaceTextUtil.java 23 Dec 2008 14:39:59 -0000 @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Tom Eicher (Avaloq Evolution AG) - block selection mode *******************************************************************************/ package org.eclipse.jface.text; @@ -16,6 +17,8 @@ import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Control; +import org.eclipse.jface.internal.text.SelectionProcessor; + import org.eclipse.jface.text.source.ILineRange; import org.eclipse.jface.text.source.LineRange; @@ -353,4 +356,33 @@ return increment; } + /** + * Returns true if the text covered by selection does not contain any + * characters in the given viewer. Note the difference to {@link ITextSelection#isEmpty()}, + * which returns true only for invalid selections. + * + * @param viewer the viewer + * @param selection the selection + * @return true if selection does not contain any text, + * false otherwise + * @throws BadLocationException if accessing the document failed + * @since 3.5 + */ + public static boolean isEmpty(ITextViewer viewer, ITextSelection selection) throws BadLocationException { + return new SelectionProcessor(viewer).isEmpty(selection); + } + + /** + * Returns the text regions covered by the given selection in the given viewer. + * + * @param viewer the viewer + * @param selection the selection + * @return the text regions corresponding to selection + * @throws BadLocationException if accessing the document failed + * @since 3.5 + */ + public static IRegion[] getCoveredRanges(ITextViewer viewer, ITextSelection selection) throws BadLocationException { + return new SelectionProcessor(viewer).getRanges(selection); + } + } Index: projection/org/eclipse/jface/text/source/projection/ProjectionViewer.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jface.text/projection/org/eclipse/jface/text/source/projection/ProjectionViewer.java,v retrieving revision 1.103 diff -u -r1.103 ProjectionViewer.java --- projection/org/eclipse/jface/text/source/projection/ProjectionViewer.java 2 Oct 2008 15:40:41 -0000 1.103 +++ projection/org/eclipse/jface/text/source/projection/ProjectionViewer.java 23 Dec 2008 14:39:58 -0000 @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Tom Eicher (Avaloq Evolution AG) - block selection mode *******************************************************************************/ package org.eclipse.jface.text.source.projection; @@ -28,6 +29,8 @@ import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.internal.text.SelectionProcessor; + import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.FindReplaceDocumentAdapter; @@ -36,9 +39,11 @@ import org.eclipse.jface.text.IDocumentListener; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ISlaveDocumentManager; +import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.ITextViewerExtension5; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.TextSelection; import org.eclipse.jface.text.TextUtilities; import org.eclipse.jface.text.projection.ProjectionDocument; import org.eclipse.jface.text.projection.ProjectionDocumentEvent; @@ -1443,34 +1448,31 @@ if (textWidget == null) return; - Point selection= null; + ITextSelection selection= null; switch (operation) { case CUT: if (redraws()) { - selection= getSelectedRange(); - if (exposeModelRange(new Region(selection.x, selection.y))) - return; - - if (selection.y == 0) + selection= (ITextSelection) getSelection(); + if (selection.getLength() == 0) copyMarkedRegion(true); else - copyToClipboard(selection.x, selection.y, true, textWidget); + copyToClipboard(selection, true, textWidget); - selection= textWidget.getSelectionRange(); - fireSelectionChanged(selection.x, selection.y); + Point range= textWidget.getSelectionRange(); + fireSelectionChanged(range.x, range.y); } break; case COPY: if (redraws()) { - selection= getSelectedRange(); - if (selection.y == 0) + selection= (ITextSelection) getSelection(); + if (selection.getLength() == 0) copyMarkedRegion(false); else - copyToClipboard(selection.x, selection.y, false, textWidget); + copyToClipboard(selection, false, textWidget); } break; @@ -1478,15 +1480,15 @@ if (redraws()) { try { - selection= getSelectedRange(); + selection= (ITextSelection) getSelection(); Point widgetSelection= textWidget.getSelectionRange(); - if (selection.y == 0 || selection.y == widgetSelection.y) + if (selection.getLength() == widgetSelection.y) getTextWidget().invokeAction(ST.DELETE_NEXT); else - deleteTextRange(selection.x, selection.y, textWidget); + deleteSelection(selection, textWidget); - selection= textWidget.getSelectionRange(); - fireSelectionChanged(selection.x, selection.y); + Point range= textWidget.getSelectionRange(); + fireSelectionChanged(range.x, range.y); } catch (BadLocationException x) { // ignore @@ -1567,21 +1569,14 @@ protected void copyMarkedRegion(boolean delete) { IRegion markedRegion= getMarkedRegion(); if (markedRegion != null) - copyToClipboard(markedRegion.getOffset(), markedRegion.getLength(), delete, getTextWidget()); + copyToClipboard(new TextSelection(getDocument(), markedRegion.getOffset(), markedRegion.getLength()), delete, getTextWidget()); } - private void copyToClipboard(int offset, int length, boolean delete, StyledText textWidget) { + private void copyToClipboard(ITextSelection selection, boolean delete, StyledText textWidget) { - String copyText= null; - - try { - IDocument document= getDocument(); - copyText= document.get(offset, length); - } catch (BadLocationException ex) { - // XXX: should log here, but JFace Text has no Log - // As a fallback solution let the widget handle this + String copyText= selection.getText(); + if (copyText == null) // selection.getText failed - backup using widget textWidget.copy(); - } if (copyText != null && copyText.equals(textWidget.getSelectionText())) { /* @@ -1616,18 +1611,23 @@ if (delete) { try { - deleteTextRange(offset, length, textWidget); + deleteSelection(selection, textWidget); } catch (BadLocationException x) { // XXX: should log here, but JFace Text has no Log } } } - private void deleteTextRange(int offset, int length, StyledText textWidget) throws BadLocationException { - getDocument().replace(offset, length, ""); //$NON-NLS-1$ - int widgetCaret= modelOffset2WidgetOffset(offset); - if (widgetCaret > -1) - textWidget.setSelection(widgetCaret); + /** + * Deletes the selection and sets the caret before the deleted range. + * + * @param selection the selection to delete + * @param textWidget the widget + * @throws BadLocationException on document access failure + * @since 3.5 + */ + private void deleteSelection(ITextSelection selection, StyledText textWidget) throws BadLocationException { + new SelectionProcessor(this).doDelete(selection); } /** Index: src/org/eclipse/jface/internal/text/SelectionProcessor.java =================================================================== RCS file: src/org/eclipse/jface/internal/text/SelectionProcessor.java diff -N src/org/eclipse/jface/internal/text/SelectionProcessor.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/jface/internal/text/SelectionProcessor.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,540 @@ +/******************************************************************************* + * Copyright (c) 2007 Avaloq Evolution AG and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tom Eicher (Avaloq Evolution AG) - initial API and implementation + *******************************************************************************/ +package org.eclipse.jface.internal.text; + +import java.util.Arrays; + +import org.eclipse.core.runtime.Assert; + +import org.eclipse.text.edits.DeleteEdit; +import org.eclipse.text.edits.InsertEdit; +import org.eclipse.text.edits.MalformedTreeException; +import org.eclipse.text.edits.MultiTextEdit; +import org.eclipse.text.edits.ReplaceEdit; +import org.eclipse.text.edits.TextEdit; + +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionProvider; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.BlockTextSelection; +import org.eclipse.jface.text.IBlockTextSelection; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.IRewriteTarget; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.ITextViewerExtension; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.TextSelection; +import org.eclipse.jface.text.TextUtilities; + +/** + * Processes {@link ITextSelection}s. + * + * @since 3.5 + */ +public final class SelectionProcessor { + private static class Implementation { + TextEdit replace(ISelection selection, String replacement) throws BadLocationException { + return new MultiTextEdit(); + } + + String getText(ISelection selection) throws BadLocationException { + return ""; //$NON-NLS-1$ + } + + boolean isEmpty(ISelection selection) throws BadLocationException { + return selection.isEmpty(); + } + + boolean isMultiline(ISelection selection) { + return false; + } + + TextEdit delete(ISelection selection) throws BadLocationException { + return replace(selection, ""); //$NON-NLS-1$ + } + + TextEdit backspace(ISelection selection) throws BadLocationException { + return replace(selection, ""); //$NON-NLS-1$ + } + + ISelection makeEmpty(ISelection selection) throws BadLocationException { + return selection; + } + + IRegion[] getRanges(ISelection selection) throws BadLocationException { + return new IRegion[0]; + } + } + + private final Implementation NULL_IMPLEMENTATION= new Implementation(); + + private final Implementation RANGE_IMPLEMENTATION= new Implementation() { + TextEdit replace(ISelection selection, String replacement) { + ITextSelection ts= (ITextSelection)selection; + return new ReplaceEdit(ts.getOffset(), ts.getLength(), replacement); + } + + String getText(ISelection selection) { + ITextSelection ts= (ITextSelection)selection; + return ts.getText(); + } + + boolean isEmpty(ISelection selection) { + ITextSelection ts= (ITextSelection)selection; + return ts.getLength() == 0; + } + + boolean isMultiline(ISelection selection) { + ITextSelection ts= (ITextSelection)selection; + return ts.getEndLine() > ts.getStartLine(); + } + + TextEdit delete(ISelection selection) { + ITextSelection ts= (ITextSelection)selection; + if (isEmpty(selection)) + return new DeleteEdit(ts.getOffset(), 1); + return new DeleteEdit(ts.getOffset(), ts.getLength()); + } + + TextEdit backspace(ISelection selection) throws BadLocationException { + ITextSelection ts= (ITextSelection)selection; + if (isEmpty(selection)) + return new DeleteEdit(ts.getOffset() - 1, 1); + return new DeleteEdit(ts.getOffset(), ts.getLength()); + } + + ISelection makeEmpty(ISelection selection) { + ITextSelection ts= (ITextSelection)selection; + return new TextSelection(fDocument, ts.getOffset(), 0); + } + + IRegion[] getRanges(ISelection selection) { + ITextSelection ts= (ITextSelection)selection; + return new IRegion[] { new Region(ts.getOffset(), ts.getLength()) }; + } + }; + + private final Implementation COLUMN_IMPLEMENTATION= new Implementation() { + TextEdit replace(ISelection selection, String replacement) throws BadLocationException { + try { + MultiTextEdit root; + IBlockTextSelection cts= (IBlockTextSelection)selection; + int startLine= cts.getStartLine(); + int endLine= cts.getEndLine(); + int startColumn= cts.getStartColumn(); + int endColumn= cts.getEndColumn(); + int visualStartColumn= computeVisualColumn(startLine, startColumn); + int visualEndColumn= computeVisualColumn(endLine, endColumn); + root= new MultiTextEdit(); + String[] delimiters= fDocument.getLegalLineDelimiters(); + + int lastDelim= 0; + for (int line= startLine; line <= endLine; line++) { + String string; + if (lastDelim == -1) { + string= ""; //$NON-NLS-1$ + } else { + int[] index= TextUtilities.indexOf(delimiters, replacement, lastDelim); + if (index[0] == -1) { + string= replacement.substring(lastDelim); + lastDelim= -1; + } else { + string= replacement.substring(lastDelim, index[0]); + lastDelim= index[0] + delimiters[index[1]].length(); + } + } + TextEdit replace= createReplaceEdit(cts, line, visualStartColumn, visualEndColumn, string); + root.addChild(replace); + } + while (lastDelim != -1) { + // more stuff to insert + String string; + int[] index= TextUtilities.indexOf(delimiters, replacement, lastDelim); + if (index[0] == -1) { + string= replacement.substring(lastDelim); + lastDelim= -1; + } else { + string= replacement.substring(lastDelim, index[0]); + lastDelim= index[0] + delimiters[index[1]].length(); + } + endLine++; + TextEdit edit; + if (endLine < fDocument.getNumberOfLines()) { + edit= createReplaceEdit(cts, endLine, visualStartColumn, visualEndColumn, string); + } else { + // insertion reaches beyond the last line + int insertLocation= root.getExclusiveEnd(); + int spaces= visualStartColumn; + char[] array= new char[spaces]; + Arrays.fill(array, ' '); + string= fDocument.getLegalLineDelimiters()[0] + String.valueOf(array) + string; + edit= new InsertEdit(insertLocation, string); + insertLocation+= string.length(); + } + root.addChild(edit); + } + return root; + } catch (MalformedTreeException x) { + Assert.isTrue(false); + return null; + } + } + + String getText(ISelection selection) throws BadLocationException { + IBlockTextSelection cts= (IBlockTextSelection)selection; + StringBuffer buf= new StringBuffer(cts.getLength()); + int startLine= cts.getStartLine(); + int endLine= cts.getEndLine(); + int startColumn= cts.getStartColumn(); + int endColumn= cts.getEndColumn(); + int visualStartColumn= computeVisualColumn(startLine, startColumn); + int visualEndColumn= computeVisualColumn(endLine, endColumn); + + for (int line= startLine; line <= endLine; line++) { + appendColumnRange(buf, line, visualStartColumn, visualEndColumn); + if (line != endLine) + buf.append(fDocument.getLineDelimiter(line)); + } + + return buf.toString(); + } + + boolean isEmpty(ISelection selection) throws BadLocationException { + IBlockTextSelection cts= (IBlockTextSelection)selection; + int startLine= cts.getStartLine(); + int endLine= cts.getEndLine(); + int startColumn= cts.getStartColumn(); + int endColumn= cts.getEndColumn(); + int visualStartColumn= computeVisualColumn(startLine, startColumn); + int visualEndColumn= computeVisualColumn(endLine, endColumn); + return visualEndColumn == visualStartColumn; + } + + boolean isMultiline(ISelection selection) { + ITextSelection ts= (ITextSelection)selection; + return ts.getEndLine() > ts.getStartLine(); + } + + TextEdit delete(ISelection selection) throws BadLocationException { + if (isEmpty(selection)) { + IBlockTextSelection cts= (IBlockTextSelection)selection; + selection= new BlockTextSelection(fDocument, cts.getStartLine(), cts.getStartColumn(), cts.getEndLine(), cts.getEndColumn() + 1, fTabWidth); + } + return replace(selection, ""); //$NON-NLS-1$ + } + + TextEdit backspace(ISelection selection) throws BadLocationException { + IBlockTextSelection cts= (IBlockTextSelection)selection; + if (isEmpty(selection) && cts.getStartColumn() > 0) { + selection= new BlockTextSelection(fDocument, cts.getStartLine(), cts.getStartColumn() - 1, cts.getEndLine(), cts.getEndColumn(), fTabWidth); + } + return replace(selection, ""); //$NON-NLS-1$ + } + + ISelection makeEmpty(ISelection selection) throws BadLocationException { + IBlockTextSelection cts= (IBlockTextSelection)selection; + int startLine= cts.getStartLine(); + int startColumn= cts.getStartColumn(); + int endLine= cts.getEndLine(); + int endColumn= computeCharacterColumn(cts.getEndLine(), computeVisualColumn(startLine, startColumn)); + return new BlockTextSelection(fDocument, startLine, startColumn, endLine, endColumn, fTabWidth); + } + + IRegion[] getRanges(ISelection selection) throws BadLocationException { + IBlockTextSelection cts= (IBlockTextSelection)selection; + final int startLine= cts.getStartLine(); + final int endLine= cts.getEndLine(); + int visualStartColumn= computeVisualColumn(startLine, cts.getStartColumn()); + int visualEndColumn= computeVisualColumn(endLine, cts.getEndColumn()); + IRegion[] ranges= new IRegion[endLine - startLine + 1]; + + for (int line= startLine; line <= endLine; line++) { + int startColumn= computeCharacterColumn(line, visualStartColumn); + int endColumn= computeCharacterColumn(line, visualEndColumn); + IRegion lineInfo= fDocument.getLineInformation(line); + int lineEnd= lineInfo.getLength(); + startColumn= Math.min(startColumn, lineEnd); + endColumn= Math.min(endColumn, lineEnd); + ranges[line - startLine]= new Region(lineInfo.getOffset() + startColumn, endColumn - startColumn); + } + + return ranges; + } + + private TextEdit createReplaceEdit(IBlockTextSelection cts, int line, int visualStartColumn, int visualEndColumn, String replacement) throws BadLocationException { + IRegion info= fDocument.getLineInformation(line); + int lineLength= info.getLength(); + String content= fDocument.get(info.getOffset(), lineLength); + int startColumn= -1; + int endColumn= -1; + int visual= 0; + for (int offset= 0; offset < lineLength; offset++) { + if (startColumn == -1 && visual >= visualStartColumn) + startColumn= offset; + if (visual == visualEndColumn) { + endColumn= offset; + break; + } + if (content.charAt(offset) == '\t') + visual+= fTabWidth - visual % fTabWidth; + else + visual++; + } + if (startColumn == -1) { + boolean materializeVirtualSpace= replacement.length() != 0; + if (materializeVirtualSpace) { + int spaces= visualStartColumn - visual; + char[] array= new char[spaces]; + Arrays.fill(array, ' '); + return new InsertEdit(info.getOffset() + lineLength, String.valueOf(array) + replacement); + } + return new MultiTextEdit(); + } + if (endColumn == -1) + endColumn= lineLength; + return new ReplaceEdit(info.getOffset() + startColumn, endColumn - startColumn, replacement); + } + + private void appendColumnRange(StringBuffer buf, int line, int visualStartColumn, int visualEndColumn) throws BadLocationException { + IRegion info= fDocument.getLineInformation(line); + int lineLength= info.getLength(); + String content= fDocument.get(info.getOffset(), lineLength); + int startColumn= -1; + int endColumn= -1; + int visual= 0; + for (int offset= 0; offset < lineLength; offset++) { + if (startColumn == -1 && visual >= visualStartColumn) + startColumn= offset; + if (visual == visualEndColumn) { + endColumn= offset; + break; + } + if (content.charAt(offset) == '\t') + visual+= fTabWidth - visual % fTabWidth; + else + visual++; + } + if (startColumn != -1) + buf.append(content.substring(startColumn, endColumn == -1 ? lineLength : endColumn)); + if (endColumn == -1) { + int spaces= visualEndColumn - Math.max(visual, visualStartColumn); + for (int i= 0; i < spaces; i++) + buf.append(' '); + } + } + + private int computeVisualColumn(final int line, final int column) throws BadLocationException { + int visualColumn= 0; + IRegion info= fDocument.getLineInformation(line); + int lineEnd= info.getLength(); + int to= Math.min(lineEnd, column); + String content= fDocument.get(info.getOffset(), info.getLength()); + for (int offset= 0; offset < to; offset++) { + if (content.charAt(offset) == '\t') + visualColumn+= fTabWidth - visualColumn % fTabWidth; + else + visualColumn++; + } + if (column > lineEnd) { + visualColumn+= column - lineEnd; // virtual spaces + } + return visualColumn; + } + + private int computeCharacterColumn(int line, int visualColumn) throws BadLocationException { + IRegion info= fDocument.getLineInformation(line); + int lineLength= info.getLength(); + String content= fDocument.get(info.getOffset(), lineLength); + int column= -1; + int visual= 0; + for (int offset= 0; offset < lineLength; offset++) { + if (column == -1 && visual >= visualColumn) + return offset; + if (content.charAt(offset) == '\t') + visual+= fTabWidth - visual % fTabWidth; + else + visual++; + } + return lineLength + visualColumn - visual; + } + }; + + private final IDocument fDocument; + + private final int fTabWidth; + + private IRewriteTarget fRewriteTarget; + + private ISelectionProvider fSelectionProvider; + + /** + * Creates a new processor on the given viewer. + * + * @param viewer the viewer + */ + public SelectionProcessor(ITextViewer viewer) { + this(viewer.getDocument(), viewer.getTextWidget().getTabs()); + if (viewer instanceof ITextViewerExtension) { + ITextViewerExtension ext= (ITextViewerExtension)viewer; + fRewriteTarget= ext.getRewriteTarget(); + } + fSelectionProvider= viewer.getSelectionProvider(); + } + + /** + * Creates a new processor on the given document and using the given tab width. + * + * @param document the document + * @param tabWidth the tabulator width in space equivalents + */ + public SelectionProcessor(IDocument document, int tabWidth) { + Assert.isNotNull(document); + Assert.isTrue(tabWidth > 0); + fDocument= document; + fTabWidth= tabWidth; + } + + /** + * Returns a text edit describing the text modification that would be executed if the delete key + * was pressed on the given selection. + * + * @param selection the selection to delete + * @return a text edit describing the operation needed to delete selection + * @throws BadLocationException if computing the edit failed + */ + public TextEdit delete(ISelection selection) throws BadLocationException { + return getImplementation(selection).delete(selection); + } + + /** + * Returns a text edit describing the text modification that would be executed if the backspace + * key was pressed on the given selection. + * + * @param selection the selection to delete + * @return a text edit describing the operation needed to delete selection + * @throws BadLocationException if computing the edit failed + */ + public TextEdit backspace(ISelection selection) throws BadLocationException { + return getImplementation(selection).backspace(selection); + } + + /** + * Returns a text edit describing the text modification that would be executed if the given + * selection was replaced by replacement. + * + * @param selection the selection to replace + * @param replacement the replacement text + * @return a text edit describing the operation needed to replace selection + * @throws BadLocationException if computing the edit failed + */ + public TextEdit replace(ISelection selection, String replacement) throws BadLocationException { + return getImplementation(selection).replace(selection, replacement); + } + + /** + * Returns the text covered by selection + * + * @param selection the selection + * @return the text covered by selection + * @throws BadLocationException if computing the edit failed + */ + public String getText(ISelection selection) throws BadLocationException { + return getImplementation(selection).getText(selection); + } + + /** + * Returns true if the text covered by selection does not contain any + * characters. Note the difference to {@link ITextSelection#isEmpty()}, which returns + * true only for invalid selections. + * + * @param selection the selection + * @return true if selection does not contain any text, + * false otherwise + * @throws BadLocationException if accessing the document failed + */ + public boolean isEmpty(ISelection selection) throws BadLocationException { + return getImplementation(selection).isEmpty(selection); + } + + /** + * Returns true if selection covers text on two or more lines, + * false otherwise. + * + * @param selection the selection + * @return true if selection covers text on two or more lines, + * false otherwise + */ + public boolean isMultiline(ISelection selection) { + return getImplementation(selection).isMultiline(selection); + } + + /** + * Returns a selection similar to selection but {@linkplain #isEmpty(ISelection) + * empty}. Typically, the selection is reduced to its left-most offset. + * + * @param selection the selection + * @return an empty variant of selection + * @throws BadLocationException if accessing the document failed + */ + public ISelection makeEmpty(ISelection selection) throws BadLocationException { + return getImplementation(selection).makeEmpty(selection); + } + + /** + * Convenience method that applies the edit returned from {@link #delete(ISelection)} to the + * underlying document. + * + * @param selection the selection to delete + * @throws BadLocationException if accessing the document failed + */ + public void doDelete(ISelection selection) throws BadLocationException { + TextEdit edit= delete(selection); + ISelection empty= makeEmpty(selection); + boolean complex= edit.hasChildren(); + if (complex && fRewriteTarget != null) + fRewriteTarget.beginCompoundChange(); + edit.apply(fDocument, TextEdit.UPDATE_REGIONS); + if (fSelectionProvider != null) + fSelectionProvider.setSelection(empty); + if (complex && fRewriteTarget != null) + fRewriteTarget.endCompoundChange(); + + } + + /** + * Returns the text regions covered by the given selection. + * + * @param selection the selection + * @return the text regions corresponding to selection + * @throws BadLocationException if accessing the document failed + */ + public IRegion[] getRanges(ISelection selection) throws BadLocationException { + return getImplementation(selection).getRanges(selection); + } + + /** + * Returns the implementation. + * + * @param selection the selection + * @return the corresponding processor implementation + */ + private Implementation getImplementation(ISelection selection) { + if (selection instanceof IBlockTextSelection) + return COLUMN_IMPLEMENTATION; + else if (selection instanceof ITextSelection) + return RANGE_IMPLEMENTATION; + else + return NULL_IMPLEMENTATION; + } +} Index: src/org/eclipse/jface/text/BlockTextSelection.java =================================================================== RCS file: src/org/eclipse/jface/text/BlockTextSelection.java diff -N src/org/eclipse/jface/text/BlockTextSelection.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/jface/text/BlockTextSelection.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,167 @@ +/******************************************************************************* + * Copyright (c) 2007 Avaloq Evolution AG and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tom Eicher (Avaloq Evolution AG) - initial API and implementation + *******************************************************************************/ +package org.eclipse.jface.text; + +import org.eclipse.core.runtime.Assert; + +import org.eclipse.jface.internal.text.SelectionProcessor; + + +/** + * Standard implementation of {@link org.eclipse.jface.text.IBlockTextSelection}. + * + * @since 3.5 + */ +public class BlockTextSelection extends TextSelection implements IBlockTextSelection { + + /** The start line. */ + private final int fStartLine; + /** The start column. */ + private final int fStartColumn; + /** The end line. */ + private final int fEndLine; + /** The end column. */ + private final int fEndColumn; + /** The tabulator width used to compute visual columns from character offsets. */ + private final int fTabWidth; + + /** + * Creates a column selection for the given lines and columns. + * + * @param document the document that this selection refers to + * @param startLine the start line + * @param startColumn the possibly virtual start column, measured in characters from the start + * of startLine + * @param endLine the inclusive end line + * @param endColumn the exclusive and possibly virtual end column, measured in characters from + * the start of endLine + * @param tabWidth the tabulator width used to compute the visual offsets from character offsets + */ + public BlockTextSelection(IDocument document, int startLine, int startColumn, int endLine, int endColumn, int tabWidth) { + super(document, computeOffset(document, startLine, startColumn), computeOffset(document, endLine, endColumn) - computeOffset(document, startLine, startColumn)); + Assert.isLegal(startLine >= 0); + Assert.isLegal(startColumn >= 0); + Assert.isLegal(endLine >= startLine); + Assert.isLegal(endColumn >= 0); + Assert.isLegal(tabWidth >= 0); + fStartLine= startLine; + fStartColumn= startColumn; + fEndLine= endLine; + fEndColumn= endColumn; + fTabWidth= tabWidth > 0 ? tabWidth : 8; // seems to be the default when StyledText.getTabs returns 0 + } + + /** + * Returns the document offset for a given tuple of line and column count. If the column count + * points beyond the end of the line, the end of the line is returned (virtual location). If the + * line points beyond the number of lines, the end of the document is returned; if the line is + * < zero, 0 is returned. + * + * @param document the document to get line information from + * @param line the line in the document, may be greater than the line count + * @param column the offset in the given line, may be greater than the line length + * @return the document offset corresponding to the line and column counts + */ + private static int computeOffset(IDocument document, int line, int column) { + try { + IRegion lineInfo= document.getLineInformation(line); + int offsetInLine= Math.min(column, lineInfo.getLength()); + return lineInfo.getOffset() + offsetInLine; + } catch (BadLocationException x) { + if (line < 0) + return 0; + return document.getLength(); + } + } + + /* + * @see org.eclipse.jface.text.TextSelection#getStartLine() + */ + public int getStartLine() { + return fStartLine; + } + + /* + * @see org.eclipse.jface.text.IColumnTextSelection#getStartColumn() + */ + public int getStartColumn() { + return fStartColumn; + } + + /* + * @see org.eclipse.jface.text.TextSelection#getEndLine() + */ + public int getEndLine() { + return fEndLine; + } + + /* + * @see org.eclipse.jface.text.IColumnTextSelection#getEndColumn() + */ + public int getEndColumn() { + return fEndColumn; + } + + /* + * @see org.eclipse.jface.text.TextSelection#getText() + */ + public String getText() { + try { + return new SelectionProcessor(fDocument, fTabWidth).getText(this); + } catch (BadLocationException x) { + return super.getText(); + } + } + + /* + * @see org.eclipse.jface.text.TextSelection#hashCode() + */ + public int hashCode() { + final int prime= 31; + int result= super.hashCode(); + result= prime * result + fEndColumn; + result= prime * result + fEndLine; + result= prime * result + fStartColumn; + result= prime * result + fStartLine; + return result; + } + + /* + * @see org.eclipse.jface.text.TextSelection#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + final BlockTextSelection other= (BlockTextSelection) obj; + if (fEndColumn != other.fEndColumn) + return false; + if (fEndLine != other.fEndLine) + return false; + if (fStartColumn != other.fStartColumn) + return false; + if (fStartLine != other.fStartLine) + return false; + return true; + } + + /* + * @see org.eclipse.jface.text.IColumnTextSelection#getRegions() + */ + public IRegion[] getRegions() { + try { + return new SelectionProcessor(fDocument, fTabWidth).getRanges(this); + } catch (BadLocationException x) { + return new IRegion[] {new Region(getOffset(), getLength())}; + } + } +} Index: src/org/eclipse/jface/text/IBlockTextSelection.java =================================================================== RCS file: src/org/eclipse/jface/text/IBlockTextSelection.java diff -N src/org/eclipse/jface/text/IBlockTextSelection.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/jface/text/IBlockTextSelection.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2007 Avaloq Evolution AG and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tom Eicher (Avaloq Evolution AG) - initial API and implementation + *******************************************************************************/ +package org.eclipse.jface.text; + +/** + * A rectangular selection in a text document. A column selection spans the visually identical range + * of columns on a contiguous range of lines. The character count of the same visually perceived + * column may not be equal for two different lines, therefore computing the set of disjunct + * character ranges covered by a column selection is influenced by the used font and tabulator + * width. Using column selections with proportional fonts may render unexpected results. + *
Virtual Spaces
+ * The {@linkplain #getStartColumn() start column} and {@linkplain #getEndColumn() end column} may + * refer to "virtual offsets" in the white space beyond the end of the line. Such an + * offset can be realized by inserting a space for each missing character. + *

+ * The {@linkplain ITextSelection#getOffset() offset} and + * {@linkplain ITextSelection#getLength() length} of an {@link IBlockTextSelection} refer to the + * smallest non-virtual range that comprises the entire rectangular selection. + *

+ *

+ * Clients may implement this interface or use the default implementation provided by + * {@link org.eclipse.jface.text.BlockTextSelection}. + *

+ * + * @see org.eclipse.jface.text.BlockTextSelection + * @since 3.5 + */ +public interface IBlockTextSelection extends ITextSelection { + /** + * Returns the column on the {@linkplain ITextSelection#getStartLine() start line} at which the + * selection starts. The returned column is a character count measured from the start of the + * line. It may be larger than the length of the line, in which case it is a virtual offset. + * + * @return the start column measured from the line start + */ + int getStartColumn(); + /** + * Returns the exclusive column on the {@linkplain ITextSelection#getEndLine() end line} at which the + * selection ends. The returned column is a character count measured from the start of the + * line. It may be larger than the length of the line, in which case it is a virtual offset. + * + * @return the end column measured from the line start + */ + int getEndColumn(); + /** + * {@inheritDoc} + *

+ * The returned text does not necessarily correspond to the total + * {@linkplain ITextSelection#getOffset() offset} and {@link ITextSelection#getLength() length}, + * as only the text within the selected columns is returned. + *

+ * Any virtual spaces beyond the end of the selected lines are + * materialized and returned as text. + *

+ * + * @see org.eclipse.jface.text.ITextSelection#getText() + */ + String getText(); + /** + * Returns a non-empty array containing the selected text range for each line covered by the + * selection. + * + * @return an array containing a the covered text range for each line covered by the receiver + */ + IRegion[] getRegions(); +}