blob: 6f05b7ef1ca64515b954d06e2f4be33a50d992b4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jface.text;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.widgets.Control;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
/**
* Manages the painters of a text viewer. Clients usually instantiate and configure
* objects of this type.
*
* @since 2.1
*/
public final class PaintManager implements KeyListener, MouseListener, ISelectionChangedListener, ITextListener, ITextInputListener {
/**
* Position updater used by the position manager. This position updater differes from the default position
* updater in that it extends a position when an insertion happens at the position's offset and right behind
* the position.
*/
static class PaintPositionUpdater extends DefaultPositionUpdater {
/**
* Creates the position updater for the given category.
*
* @param category the position category
*/
protected PaintPositionUpdater(String category) {
super(category);
}
/**
* If an insertion happens at a position's offset, the
* position is extended rather than shifted. Also, if something is added
* right behind the end of the position, the position is extended rather
* than kept stable.
*/
protected void adaptToInsert() {
int myStart= fPosition.offset;
int myEnd= fPosition.offset + fPosition.length;
myEnd= Math.max(myStart, myEnd);
int yoursStart= fOffset;
int yoursEnd= fOffset + fReplaceLength;// - 1;
yoursEnd= Math.max(yoursStart, yoursEnd);
if (myEnd < yoursStart)
return;
if (myStart <= yoursStart)
fPosition.length += fReplaceLength;
else
fPosition.offset += fReplaceLength;
}
}
/**
* The paint position manager used by this paint manager. The paint position
* manager is installed on a single document and control the creation/disposed
* and updating of a position category that will be used for managing positions.
*/
static class PositionManager implements IPaintPositionManager {
/** The document this positon manager works on */
private IDocument fDocument;
/** The position updater used for the managing position category */
private IPositionUpdater fPositionUpdater;
/** The managing position category */
private String fCategory;
/**
* Creates a new position manager. Initializes the managing
* position category using its class name and its hash value.
*/
public PositionManager() {
fCategory= getClass().getName() + hashCode();
fPositionUpdater= new PaintPositionUpdater(fCategory);
}
/**
* Installs this position manager in the given document. The position manager stays
* active until <code>uninstall</code> or <code>dispose</code>
* is called.
*
* @param document the document to be installed on
*/
public void install(IDocument document) {
fDocument= document;
fDocument.addPositionCategory(fCategory);
fDocument.addPositionUpdater(fPositionUpdater);
}
/**
* Diposes this position manager. The position manager is automatically
* uninstalled from the document it has previously been installed
* on.
*/
public void dispose() {
uninstall(fDocument);
}
/**
* Uninstalls this position manager form the given document. If the position
* manager has no been installed on this document, this method is without effect.
*
* @param document the document form which to uninstall
*/
public void uninstall(IDocument document) {
if (document == fDocument && document != null) {
try {
fDocument.removePositionUpdater(fPositionUpdater);
fDocument.removePositionCategory(fCategory);
} catch (BadPositionCategoryException x) {
// should not happen
}
fDocument= null;
}
}
/*
* @see IPositionManager#addManagedPosition(Position)
*/
public void managePosition(Position position) {
try {
fDocument.addPosition(fCategory, position);
} catch (BadPositionCategoryException x) {
// should not happen
} catch (BadLocationException x) {
// should not happen
}
}
/*
* @see IPositionManager#removeManagedPosition(Position)
*/
public void unmanagePosition(Position position) {
try {
fDocument.removePosition(fCategory, position);
} catch (BadPositionCategoryException x) {
// should not happen
}
}
}
/** The painters managed by this paint manager. */
private List fPainters= new ArrayList(2);
/** The position manager used by this paint manager */
private PositionManager fManager;
/** The associated text viewer */
private ITextViewer fTextViewer;
/**
* Creates a new paint manager for the given text viewer.
*
* @param textViewer the text viewer associated to this newly created paint manager
*/
public PaintManager(ITextViewer textViewer) {
fTextViewer= textViewer;
}
/**
* Adds the given painter to the list of painters managed by this paint manager.
* If the painter is already registered with this paint manager, this method is
* without effect.
*
* @param painter the painter to be added
*/
public void addPainter(IPainter painter) {
if (!fPainters.contains(painter)) {
fPainters.add(painter);
if (fPainters.size() == 1)
install();
painter.setPositionManager(fManager);
painter.paint(IPainter.INTERNAL);
}
}
/**
* Removes the given painter from the list of painters managed by this
* paint manager. If the painter has not previously been added to this
* paint manager, this method is without effect.
*
* @param painter the painter to be removed
*/
public void removePainter(IPainter painter) {
if (fPainters.remove(painter))
painter.setPositionManager(null);
if (fPainters.size() == 0)
dispose();
}
/**
* Installs/activates this paint manager. Is called as soon as the
* first painter is to be managed by this paint manager.
*/
private void install() {
fManager= new PositionManager();
if (fTextViewer.getDocument() != null)
fManager.install(fTextViewer.getDocument());
fTextViewer.addTextInputListener(this);
ISelectionProvider provider= fTextViewer.getSelectionProvider();
provider.addSelectionChangedListener(this);
fTextViewer.addTextListener(this);
StyledText text= fTextViewer.getTextWidget();
text.addKeyListener(this);
text.addMouseListener(this);
}
/**
* Disposes this paint manager. The paint manager uninstalls itself
* and clears all registered painters. This method is also called when the
* last painter is removed from the list of managed painters.
*/
public void dispose() {
if (fManager != null) {
fManager.dispose();
fManager= null;
}
for (Iterator e = fPainters.iterator(); e.hasNext();)
((IPainter) e.next()).dispose();
fPainters.clear();
fTextViewer.removeTextInputListener(this);
ISelectionProvider provider= fTextViewer.getSelectionProvider();
if (provider != null)
provider.removeSelectionChangedListener(this);
fTextViewer.removeTextListener(this);
StyledText text= fTextViewer.getTextWidget();
if (text != null && !text.isDisposed()) {
text.removeKeyListener(this);
text.removeMouseListener(this);
}
}
/**
* Triggers all registered painters for the given reason.
*
* @param reason the reason
* @see IPainter
*/
private void paint(int reason) {
for (Iterator e = fPainters.iterator(); e.hasNext();)
((IPainter) e.next()).paint(reason);
}
/*
* @see KeyListener#keyPressed(KeyEvent)
*/
public void keyPressed(KeyEvent e) {
paint(IPainter.KEY_STROKE);
}
/*
* @see KeyListener#keyReleased(KeyEvent)
*/
public void keyReleased(KeyEvent e) {
}
/*
* @see MouseListener#mouseDoubleClick(MouseEvent)
*/
public void mouseDoubleClick(MouseEvent e) {
}
/*
* @see MouseListener#mouseDown(MouseEvent)
*/
public void mouseDown(MouseEvent e) {
paint(IPainter.MOUSE_BUTTON);
}
/*
* @see MouseListener#mouseUp(MouseEvent)
*/
public void mouseUp(MouseEvent e) {
}
/*
* @see ISelectionChangedListener#selectionChanged(SelectionChangedEvent)
*/
public void selectionChanged(SelectionChangedEvent event) {
paint(IPainter.SELECTION);
}
/*
* @see ITextListener#textChanged(TextEvent)
*/
public void textChanged(TextEvent event) {
if (!event.getViewerRedrawState())
return;
Control control= fTextViewer.getTextWidget();
if (control != null) {
control.getDisplay().asyncExec(new Runnable() {
public void run() {
if (fTextViewer != null)
paint(IPainter.TEXT_CHANGE);
}
});
}
}
/*
* @see ITextInputListener#inputDocumentAboutToBeChanged(IDocument, IDocument)
*/
public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
if (oldInput != null) {
for (Iterator e = fPainters.iterator(); e.hasNext();)
((IPainter) e.next()).deactivate(false);
fManager.uninstall(oldInput);
}
}
/*
* @see ITextInputListener#inputDocumentChanged(IDocument, IDocument)
*/
public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
if (newInput != null) {
fManager.install(newInput);
paint(IPainter.TEXT_CHANGE);
}
}
}