KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > ui > text > SmartBackspaceManager


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.ui.text;
12
13 import java.util.HashMap JavaDoc;
14 import java.util.Iterator JavaDoc;
15 import java.util.Map JavaDoc;
16
17 import org.eclipse.text.edits.MalformedTreeException;
18 import org.eclipse.text.edits.TextEdit;
19
20 import org.eclipse.core.runtime.Assert;
21
22 import org.eclipse.swt.SWT;
23 import org.eclipse.swt.custom.VerifyKeyListener;
24 import org.eclipse.swt.events.VerifyEvent;
25 import org.eclipse.swt.graphics.Point;
26
27 import org.eclipse.jface.text.BadLocationException;
28 import org.eclipse.jface.text.IDocument;
29 import org.eclipse.jface.text.IRegion;
30 import org.eclipse.jface.text.ITextViewer;
31 import org.eclipse.jface.text.ITextViewerExtension;
32 import org.eclipse.jface.text.TextViewer;
33
34 import org.eclipse.jdt.internal.ui.text.TypingRun.ChangeType;
35
36
37
38 /**
39  * Installs as a verify key listener on a viewer and overwrites the behavior
40  * of the backspace key. Clients may register undo specifications for certain
41  * offsets in a document. The <code>SmartBackspaceManager</code> will manage the
42  * specifications and execute the contained <code>TextEdit</code>s when backspace
43  * is pressed at the given offset and the specification is still valid.
44  * <p>
45  * Undo specifications are removed after a number of typing runs.
46  * </p>
47  *
48  * @since 3.0
49  */

50 public class SmartBackspaceManager {
51     /* independent of JDT - may be moved to jface.text */
52
53     /**
54      * An undo specification describes the change that should be executed if
55      * backspace is pressed at its trigger offset.
56      *
57      * @since 3.0
58      */

59     public static final class UndoSpec {
60         private final int triggerOffset;
61         private final IRegion selection;
62         private final TextEdit[] undoEdits;
63         private final UndoSpec child;
64         int lives;
65
66         /**
67          * Creates a new spec. A specification consists of a number of
68          * <code>TextEdit</code>s that will be executed when backspace is
69          * pressed at <code>triggerOffset</code>. The spec will be removed
70          * when it is executed, or if more than <code>lives</code>
71          * <code>TypingRun</code>s have ended after registering the spec.
72          * <p>
73          * Optionally, a child specification can be registered. After executing
74          * the spec, the child spec will be registered with the manager. This allows
75          * to create chains of <code>UndoSpec</code>s that will be executed upon
76          * repeated pressing of backspace.
77          * </p>
78          *
79          * @param triggerOffset the offset where this spec is active
80          * @param selection the selection after executing the undo spec
81          * @param edits the <code>TextEdit</code>s to perform when executing
82          * the spec
83          * @param lives the number of <code>TypingRun</code>s before removing
84          * the spec
85          * @param child a child specification that will be registered after
86          * executing this spec, or <code>null</code>
87          */

88         public UndoSpec(int triggerOffset, IRegion selection, TextEdit[] edits, int lives, UndoSpec child) {
89             Assert.isLegal(triggerOffset >= 0);
90             Assert.isLegal(selection != null);
91             Assert.isLegal(lives >= 0);
92             Assert.isLegal(edits != null);
93             Assert.isLegal(edits.length > 0);
94             for (int i= 0; i < edits.length; i++) {
95                 Assert.isLegal(edits[i] != null);
96             }
97
98             this.triggerOffset= triggerOffset;
99             this.selection= selection;
100             this.undoEdits= edits;
101             this.lives= lives;
102             this.child= child;
103         }
104     }
105
106
107     private class BackspaceListener implements VerifyKeyListener {
108
109         /*
110          * @see org.eclipse.swt.custom.VerifyKeyListener#verifyKey(org.eclipse.swt.events.VerifyEvent)
111          */

112         public void verifyKey(VerifyEvent event) {
113             if (fViewer != null && isBackspace(event)) {
114                 int offset= getCaretOffset();
115                 UndoSpec spec= removeEdit(offset);
116                 if (spec != null) {
117                     try {
118                         beginChange();
119                         for (int i= 0; i < spec.undoEdits.length; i++) {
120                             spec.undoEdits[i].apply(getDocument(), TextEdit.UPDATE_REGIONS);
121                         }
122                         fViewer.setSelectedRange(spec.selection.getOffset(), spec.selection.getLength());
123                         if (spec.child != null)
124                             register(spec.child);
125                     } catch (MalformedTreeException e) {
126                         // fall back to standard bs
127
return;
128                     } catch (BadLocationException e) {
129                         // fall back to standard bs
130
return;
131                     } finally {
132                         endChange();
133                     }
134                     event.doit= false;
135                 }
136
137             }
138         }
139
140         private void beginChange() {
141             ITextViewer viewer= fViewer;
142             if (viewer instanceof TextViewer) {
143                 TextViewer v= (TextViewer) viewer;
144                 v.getRewriteTarget().beginCompoundChange();
145             }
146         }
147
148         private void endChange() {
149             ITextViewer viewer= fViewer;
150             if (viewer instanceof TextViewer) {
151                 TextViewer v= (TextViewer) viewer;
152                 v.getRewriteTarget().endCompoundChange();
153             }
154         }
155
156         private boolean isBackspace(VerifyEvent event) {
157             return event.doit == true && event.character == SWT.BS && event.stateMask == 0;
158         }
159
160         private int getCaretOffset() {
161             ITextViewer viewer= fViewer;
162             Point point= viewer.getSelectedRange();
163             return point.x;
164         }
165
166     }
167
168     private ITextViewer fViewer;
169     private BackspaceListener fBackspaceListener;
170     private Map JavaDoc fSpecs;
171     private TypingRunDetector fRunDetector;
172     private ITypingRunListener fRunListener;
173
174     /**
175      * Registers an undo specification with this manager.
176      *
177      * @param spec the specification to register
178      * @throws IllegalStateException if the manager is not installed
179      */

180     public void register(UndoSpec spec) {
181         if (fViewer == null)
182             throw new IllegalStateException JavaDoc();
183
184         ensureListenerInstalled();
185         addEdit(spec);
186     }
187
188     private void addEdit(UndoSpec spec) {
189         Integer JavaDoc i= new Integer JavaDoc(spec.triggerOffset);
190         fSpecs.put(i, spec);
191     }
192
193     private UndoSpec removeEdit(int offset) {
194         Integer JavaDoc i= new Integer JavaDoc(offset);
195         UndoSpec spec= (UndoSpec) fSpecs.remove(i);
196         return spec;
197     }
198
199     private void ensureListenerInstalled() {
200         if (fBackspaceListener == null) {
201             fBackspaceListener= new BackspaceListener();
202             ITextViewer viewer= fViewer;
203             if (viewer instanceof ITextViewerExtension)
204                 ((ITextViewerExtension) viewer).prependVerifyKeyListener(fBackspaceListener);
205             else
206                 viewer.getTextWidget().addVerifyKeyListener(fBackspaceListener);
207         }
208     }
209
210     private void ensureListenerRemoved() {
211         if (fBackspaceListener != null) {
212             ITextViewer viewer= fViewer;
213             if (viewer instanceof ITextViewerExtension)
214                 ((ITextViewerExtension) viewer).removeVerifyKeyListener(fBackspaceListener);
215             else
216                 viewer.getTextWidget().removeVerifyKeyListener(fBackspaceListener);
217             fBackspaceListener= null;
218         }
219     }
220
221     private IDocument getDocument() {
222         return fViewer.getDocument();
223     }
224
225     /**
226      * Installs the receiver on a text viewer.
227      *
228      * @param viewer
229      */

230     public void install(ITextViewer viewer) {
231         Assert.isLegal(viewer != null);
232
233         fViewer= viewer;
234         fSpecs= new HashMap JavaDoc();
235         fRunDetector= new TypingRunDetector();
236         fRunDetector.install(viewer);
237         fRunListener= new ITypingRunListener() {
238
239             /*
240              * @see org.eclipse.jface.text.TypingRunDetector.ITypingRunListener#typingRunStarted(org.eclipse.jface.text.TypingRunDetector.TypingRun)
241              */

242             public void typingRunStarted(TypingRun run) {
243             }
244
245             /*
246              * @see org.eclipse.jface.text.TypingRunDetector.ITypingRunListener#typingRunEnded(org.eclipse.jface.text.TypingRunDetector.TypingRun)
247              */

248             public void typingRunEnded(TypingRun run, ChangeType reason) {
249                 if (reason == TypingRun.SELECTION)
250                     fSpecs.clear();
251                 else
252                     prune();
253             }
254         };
255         fRunDetector.addTypingRunListener(fRunListener);
256     }
257
258     private void prune() {
259         for (Iterator JavaDoc it= fSpecs.values().iterator(); it.hasNext();) {
260             UndoSpec spec= (UndoSpec) it.next();
261             if (--spec.lives < 0)
262                 it.remove();
263         }
264     }
265
266     /**
267      * Uninstalls the receiver. No undo specifications may be registered on an
268      * uninstalled manager.
269      */

270     public void uninstall() {
271         if (fViewer != null) {
272             fRunDetector.removeTypingRunListener(fRunListener);
273             fRunDetector.uninstall();
274             fRunDetector= null;
275             ensureListenerRemoved();
276             fViewer= null;
277         }
278     }
279 }
280
Popular Tags