001: /*******************************************************************************
002: * Copyright (c) 2000, 2005 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jface.text;
011:
012: import java.util.ArrayList;
013: import java.util.Iterator;
014: import java.util.List;
015:
016: import org.eclipse.swt.custom.StyledText;
017: import org.eclipse.swt.events.KeyEvent;
018: import org.eclipse.swt.events.KeyListener;
019: import org.eclipse.swt.events.MouseEvent;
020: import org.eclipse.swt.events.MouseListener;
021: import org.eclipse.swt.widgets.Control;
022:
023: import org.eclipse.jface.viewers.ISelectionChangedListener;
024: import org.eclipse.jface.viewers.ISelectionProvider;
025: import org.eclipse.jface.viewers.SelectionChangedEvent;
026:
027: /**
028: * Manages the {@link org.eclipse.jface.text.IPainter} object registered with an
029: * {@link org.eclipse.jface.text.ITextViewer}.
030: * <p>
031: * Clients usually instantiate and configure objects of this type.</p>
032: *
033: * @since 2.1
034: */
035: public final class PaintManager implements KeyListener, MouseListener,
036: ISelectionChangedListener, ITextListener, ITextInputListener {
037:
038: /**
039: * Position updater used by the position manager. This position updater differs from the default position
040: * updater in that it extends a position when an insertion happens at the position's offset and right behind
041: * the position.
042: */
043: static class PaintPositionUpdater extends DefaultPositionUpdater {
044:
045: /**
046: * Creates the position updater for the given category.
047: *
048: * @param category the position category
049: */
050: protected PaintPositionUpdater(String category) {
051: super (category);
052: }
053:
054: /**
055: * If an insertion happens at a position's offset, the
056: * position is extended rather than shifted. Also, if something is added
057: * right behind the end of the position, the position is extended rather
058: * than kept stable.
059: */
060: protected void adaptToInsert() {
061:
062: int myStart = fPosition.offset;
063: int myEnd = fPosition.offset + fPosition.length;
064: myEnd = Math.max(myStart, myEnd);
065:
066: int yoursStart = fOffset;
067: int yoursEnd = fOffset + fReplaceLength;// - 1;
068: yoursEnd = Math.max(yoursStart, yoursEnd);
069:
070: if (myEnd < yoursStart)
071: return;
072:
073: if (myStart <= yoursStart)
074: fPosition.length += fReplaceLength;
075: else
076: fPosition.offset += fReplaceLength;
077: }
078: }
079:
080: /**
081: * The paint position manager used by this paint manager. The paint position
082: * manager is installed on a single document and control the creation/disposed
083: * and updating of a position category that will be used for managing positions.
084: */
085: static class PositionManager implements IPaintPositionManager {
086:
087: // /** The document this position manager works on */
088: private IDocument fDocument;
089: /** The position updater used for the managing position category */
090: private IPositionUpdater fPositionUpdater;
091: /** The managing position category */
092: private String fCategory;
093:
094: /**
095: * Creates a new position manager. Initializes the managing
096: * position category using its class name and its hash value.
097: */
098: public PositionManager() {
099: fCategory = getClass().getName() + hashCode();
100: fPositionUpdater = new PaintPositionUpdater(fCategory);
101: }
102:
103: /**
104: * Installs this position manager in the given document. The position manager stays
105: * active until <code>uninstall</code> or <code>dispose</code>
106: * is called.
107: *
108: * @param document the document to be installed on
109: */
110: public void install(IDocument document) {
111: fDocument = document;
112: fDocument.addPositionCategory(fCategory);
113: fDocument.addPositionUpdater(fPositionUpdater);
114: }
115:
116: /**
117: * Disposes this position manager. The position manager is automatically
118: * removed from the document it has previously been installed
119: * on.
120: */
121: public void dispose() {
122: uninstall(fDocument);
123: }
124:
125: /**
126: * Uninstalls this position manager form the given document. If the position
127: * manager has no been installed on this document, this method is without effect.
128: *
129: * @param document the document form which to uninstall
130: */
131: public void uninstall(IDocument document) {
132: if (document == fDocument && document != null) {
133: try {
134: fDocument.removePositionUpdater(fPositionUpdater);
135: fDocument.removePositionCategory(fCategory);
136: } catch (BadPositionCategoryException x) {
137: // should not happen
138: }
139: fDocument = null;
140: }
141: }
142:
143: /*
144: * @see IPositionManager#addManagedPosition(Position)
145: */
146: public void managePosition(Position position) {
147: try {
148: fDocument.addPosition(fCategory, position);
149: } catch (BadPositionCategoryException x) {
150: // should not happen
151: } catch (BadLocationException x) {
152: // should not happen
153: }
154: }
155:
156: /*
157: * @see IPositionManager#removeManagedPosition(Position)
158: */
159: public void unmanagePosition(Position position) {
160: try {
161: fDocument.removePosition(fCategory, position);
162: } catch (BadPositionCategoryException x) {
163: // should not happen
164: }
165: }
166: }
167:
168: /** The painters managed by this paint manager. */
169: private List fPainters = new ArrayList(2);
170: /** The position manager used by this paint manager */
171: private PositionManager fManager;
172: /** The associated text viewer */
173: private ITextViewer fTextViewer;
174:
175: /**
176: * Creates a new paint manager for the given text viewer.
177: *
178: * @param textViewer the text viewer associated to this newly created paint manager
179: */
180: public PaintManager(ITextViewer textViewer) {
181: fTextViewer = textViewer;
182: }
183:
184: /**
185: * Adds the given painter to the list of painters managed by this paint manager.
186: * If the painter is already registered with this paint manager, this method is
187: * without effect.
188: *
189: * @param painter the painter to be added
190: */
191: public void addPainter(IPainter painter) {
192: if (!fPainters.contains(painter)) {
193: fPainters.add(painter);
194: if (fPainters.size() == 1)
195: install();
196: painter.setPositionManager(fManager);
197: painter.paint(IPainter.INTERNAL);
198: }
199: }
200:
201: /**
202: * Removes the given painter from the list of painters managed by this
203: * paint manager. If the painter has not previously been added to this
204: * paint manager, this method is without effect.
205: *
206: * @param painter the painter to be removed
207: */
208: public void removePainter(IPainter painter) {
209: if (fPainters.remove(painter)) {
210: painter.deactivate(true);
211: painter.setPositionManager(null);
212: }
213: if (fPainters.size() == 0)
214: dispose();
215: }
216:
217: /**
218: * Installs/activates this paint manager. Is called as soon as the
219: * first painter is to be managed by this paint manager.
220: */
221: private void install() {
222:
223: fManager = new PositionManager();
224: if (fTextViewer.getDocument() != null)
225: fManager.install(fTextViewer.getDocument());
226:
227: fTextViewer.addTextInputListener(this );
228:
229: addListeners();
230: }
231:
232: /**
233: * Installs our listener set on the text viewer and the text widget,
234: * respectively.
235: */
236: private void addListeners() {
237: ISelectionProvider provider = fTextViewer
238: .getSelectionProvider();
239: provider.addSelectionChangedListener(this );
240:
241: fTextViewer.addTextListener(this );
242:
243: StyledText text = fTextViewer.getTextWidget();
244: text.addKeyListener(this );
245: text.addMouseListener(this );
246: }
247:
248: /**
249: * Disposes this paint manager. The paint manager uninstalls itself
250: * and clears all registered painters. This method is also called when the
251: * last painter is removed from the list of managed painters.
252: */
253: public void dispose() {
254:
255: if (fManager != null) {
256: fManager.dispose();
257: fManager = null;
258: }
259:
260: for (Iterator e = fPainters.iterator(); e.hasNext();)
261: ((IPainter) e.next()).dispose();
262: fPainters.clear();
263:
264: fTextViewer.removeTextInputListener(this );
265:
266: removeListeners();
267: }
268:
269: /**
270: * Removes our set of listeners from the text viewer and widget,
271: * respectively.
272: */
273: private void removeListeners() {
274: ISelectionProvider provider = fTextViewer
275: .getSelectionProvider();
276: if (provider != null)
277: provider.removeSelectionChangedListener(this );
278:
279: fTextViewer.removeTextListener(this );
280:
281: StyledText text = fTextViewer.getTextWidget();
282: if (text != null && !text.isDisposed()) {
283: text.removeKeyListener(this );
284: text.removeMouseListener(this );
285: }
286: }
287:
288: /**
289: * Triggers all registered painters for the given reason.
290: *
291: * @param reason the reason
292: * @see IPainter
293: */
294: private void paint(int reason) {
295: for (Iterator e = fPainters.iterator(); e.hasNext();)
296: ((IPainter) e.next()).paint(reason);
297: }
298:
299: /*
300: * @see KeyListener#keyPressed(KeyEvent)
301: */
302: public void keyPressed(KeyEvent e) {
303: paint(IPainter.KEY_STROKE);
304: }
305:
306: /*
307: * @see KeyListener#keyReleased(KeyEvent)
308: */
309: public void keyReleased(KeyEvent e) {
310: }
311:
312: /*
313: * @see MouseListener#mouseDoubleClick(MouseEvent)
314: */
315: public void mouseDoubleClick(MouseEvent e) {
316: }
317:
318: /*
319: * @see MouseListener#mouseDown(MouseEvent)
320: */
321: public void mouseDown(MouseEvent e) {
322: paint(IPainter.MOUSE_BUTTON);
323: }
324:
325: /*
326: * @see MouseListener#mouseUp(MouseEvent)
327: */
328: public void mouseUp(MouseEvent e) {
329: }
330:
331: /*
332: * @see ISelectionChangedListener#selectionChanged(SelectionChangedEvent)
333: */
334: public void selectionChanged(SelectionChangedEvent event) {
335: paint(IPainter.SELECTION);
336: }
337:
338: /*
339: * @see ITextListener#textChanged(TextEvent)
340: */
341: public void textChanged(TextEvent event) {
342:
343: if (!event.getViewerRedrawState())
344: return;
345:
346: Control control = fTextViewer.getTextWidget();
347: if (control != null) {
348: control.getDisplay().asyncExec(new Runnable() {
349: public void run() {
350: if (fTextViewer != null)
351: paint(IPainter.TEXT_CHANGE);
352: }
353: });
354: }
355: }
356:
357: /*
358: * @see ITextInputListener#inputDocumentAboutToBeChanged(IDocument, IDocument)
359: */
360: public void inputDocumentAboutToBeChanged(IDocument oldInput,
361: IDocument newInput) {
362: if (oldInput != null) {
363: for (Iterator e = fPainters.iterator(); e.hasNext();)
364: ((IPainter) e.next()).deactivate(false);
365: fManager.uninstall(oldInput);
366: removeListeners();
367: }
368: }
369:
370: /*
371: * @see ITextInputListener#inputDocumentChanged(IDocument, IDocument)
372: */
373: public void inputDocumentChanged(IDocument oldInput,
374: IDocument newInput) {
375: if (newInput != null) {
376: fManager.install(newInput);
377: paint(IPainter.TEXT_CHANGE);
378: addListeners();
379: }
380: }
381: }
|