org.cfeclipse.cfml.editors.decoration.MatchingCharacterPainter.java Source code

Java tutorial

Introduction

Here is the source code for org.cfeclipse.cfml.editors.decoration.MatchingCharacterPainter.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2004 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.cfeclipse.cfml.editors.decoration;

import org.cfeclipse.cfml.CFMLPlugin;
import org.cfeclipse.cfml.preferences.EditorPreferenceConstants;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IPaintPositionManager;
import org.eclipse.jface.text.IPainter;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewerExtension5;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.source.ICharacterPairMatcher;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.ui.internal.editors.text.EditorsPlugin;

/**
 * Highlights the peer character matching the character near the caret position.
 * This painter can be configured with an
 * {@link org.eclipse.jface.text.source.ICharacterPairMatcher}.
 * <p>
 * Clients instantiate and configure object of this class.
 * 
 * @since 2.1
 */
public final class MatchingCharacterPainter implements IPainter, PaintListener {

    /** Indicates whether this painter is active */
    private boolean fIsActive = false;
    /** The source viewer this painter is associated with */
    private ISourceViewer fSourceViewer;
    /** The viewer's widget */
    private StyledText fTextWidget;
    /** The color in which to highlight the peer character */
    private Color fColor;
    /** The paint position manager */
    private IPaintPositionManager fPaintPositionManager;
    /** The strategy for finding matching characters */
    private ICharacterPairMatcher fMatcher;
    /** The position tracking the matching characters */
    private Position fPairPosition = new Position(0, 0);
    /** The anchor indicating whether the character is left or right of the caret */
    private int fAnchor;

    /**
     * Creates a new MatchingCharacterPainter for the given source viewer using
     * the given character pair matcher. The character matcher is not adopted by
     * this painter. Thus,  it is not disposed. However, this painter requires
     * exclusive access to the given pair matcher.
     * 
     * @param sourceViewer
     * @param matcher
     */
    public MatchingCharacterPainter(ISourceViewer sourceViewer, ICharacterPairMatcher matcher) {
        fSourceViewer = sourceViewer;
        fMatcher = matcher;
        fTextWidget = sourceViewer.getTextWidget();
    }

    /**
     * Sets the color in which to highlight the match character.
     * 
     * @param color the color
     */
    public void setColor(Color color) {
        fColor = color;
    }

    /*
     * @see org.eclipse.jface.text.IPainter#dispose()
     */
    public void dispose() {
        if (fMatcher != null) {
            fMatcher.clear();
            fMatcher = null;
        }

        fColor = null;
        fTextWidget = null;
    }

    /*
     * @see org.eclipse.jface.text.IPainter#deactivate(boolean)
     */
    public void deactivate(boolean redraw) {
        if (fIsActive) {
            fIsActive = false;
            fTextWidget.removePaintListener(this);
            if (fPaintPositionManager != null)
                fPaintPositionManager.unmanagePosition(fPairPosition);
            if (redraw)
                handleDrawRequest(null);
        }
    }

    /*
     * @see org.eclipse.swt.events.PaintListener#paintControl(org.eclipse.swt.events.PaintEvent)
     */
    public void paintControl(PaintEvent event) {
        if (fTextWidget != null)
            handleDrawRequest(event.gc);
    }

    /**
     * Handles a redraw request.
     * 
     * @param gc the GC to draw into.
     */
    private void handleDrawRequest(GC gc) {

        if (fPairPosition.isDeleted)
            return;

        int offset = fPairPosition.getOffset();
        int length = fPairPosition.getLength();
        if (length < 1)
            return;

        if (fSourceViewer instanceof ITextViewerExtension5) {
            ITextViewerExtension5 extension = (ITextViewerExtension5) fSourceViewer;
            IRegion widgetRange = extension.modelRange2WidgetRange(new Region(offset, length));

            if (widgetRange == null)
                return;

            try {
                // don't draw if the pair position is really hidden and widgetRange just
                // marks the coverage around it.
                IDocument doc = fSourceViewer.getDocument();
                int startLine = doc.getLineOfOffset(offset);
                int endLine = doc.getLineOfOffset(offset + length);
                if (extension.modelLine2WidgetLine(startLine) == -1
                        || extension.modelLine2WidgetLine(endLine) == -1)
                    return;
            } catch (BadLocationException e) {
                return;
            }

            offset = widgetRange.getOffset();
            length = widgetRange.getLength();

        } else {
            IRegion region = fSourceViewer.getVisibleRegion();
            if (region.getOffset() > offset || region.getOffset() + region.getLength() < offset + length)
                return;
            offset -= region.getOffset();
        }

        int endOffset = offset + length - 1;
        draw(gc, offset, 1);
        draw(gc, endOffset, 1);

    }

    /**
     * Highlights the given widget region.
     * 
     * @param gc the GC to draw into
     * @param offset the offset of the widget region
     * @param length the length of the widget region
     */
    private void draw(GC gc, int offset, int length) {
        if (gc != null) {
            Point left = fTextWidget.getLocationAtOffset(offset);
            Point right = fTextWidget.getLocationAtOffset(offset + length);

            IPreferenceStore store = CFMLPlugin.getDefault().getPreferenceStore();
            int style = store.getInt(EditorPreferenceConstants.P_BRACKET_MATCHING_STYLE);

            switch (style) {
            case EditorPreferenceConstants.BRACKET_MATCHING_OUTLINE: {
                //draw box around character
                gc.setForeground(fColor);
                gc.drawRectangle(left.x, left.y, right.x - left.x - 1, fTextWidget.getLineHeight() - 1);
                break;
            }
            case EditorPreferenceConstants.BRACKET_MATCHING_BACKGROUND: {
                //Paint a background on the character
                gc.setBackground(fColor);
                gc.drawText(fTextWidget.getText(offset, offset), left.x, left.y + 1);
                break;
            }
            case EditorPreferenceConstants.BRACKET_MATCHING_BOLD: {
                int caret = fTextWidget.getCaretOffset();
                int lineIndex = fTextWidget.getLineAtOffset(caret);
                int lineStart = fTextWidget.getOffsetAtLine(lineIndex);
                int lineEnd = -1;
                if (lineIndex == fTextWidget.getLineCount()) {
                    lineEnd = fTextWidget.getText().length() - 1;
                } else {
                    lineEnd = fTextWidget.getText().indexOf(fTextWidget.getLineDelimiter(), lineStart);
                }
                if (offset >= lineStart && offset <= lineEnd) {
                    RGB rgb = PreferenceConverter.getColor(store, EditorPreferenceConstants.P_CURRENT_LINE_COLOR);
                    Color c = EditorsPlugin.getDefault().getSharedTextColors().getColor(rgb);
                    gc.setBackground(c);
                } else {
                    gc.setBackground(fTextWidget.getBackground());
                }

                gc.setForeground(fColor);
                Font oldFont = gc.getFont();
                FontData[] data = gc.getFont().getFontData();
                data[0].setStyle(SWT.BOLD);

                Font font = new Font(fTextWidget.getDisplay(), data);
                gc.setFont(font);
                gc.drawText(fTextWidget.getText(offset, offset), left.x, left.y + 1);
                gc.setFont(oldFont);
                font.dispose();
                break;
            }
            }

        } else {
            fTextWidget.redrawRange(offset, length, true);
        }
    }

    /*
     * @see org.eclipse.jface.text.IPainter#paint(int)
     */
    public void paint(int reason) {

        IDocument document = fSourceViewer.getDocument();
        if (document == null) {
            deactivate(false);
            return;
        }

        Point selection = fSourceViewer.getSelectedRange();
        if (selection.y > 0) {
            deactivate(true);
            return;
        }

        IRegion pair = fMatcher.match(document, selection.x);
        if (pair == null) {
            deactivate(true);
            return;
        }

        if (fIsActive) {

            if (IPainter.CONFIGURATION == reason) {

                // redraw current highlighting
                handleDrawRequest(null);

            } else if (pair.getOffset() != fPairPosition.getOffset()
                    || pair.getLength() != fPairPosition.getLength() || fMatcher.getAnchor() != fAnchor) {

                // otherwise only do something if position is different

                // remove old highlighting
                handleDrawRequest(null);
                // update position
                fPairPosition.isDeleted = false;
                fPairPosition.offset = pair.getOffset();
                fPairPosition.length = pair.getLength();
                fAnchor = fMatcher.getAnchor();
                // apply new highlighting
                handleDrawRequest(null);

            }
        } else {

            fIsActive = true;

            fPairPosition.isDeleted = false;
            fPairPosition.offset = pair.getOffset();
            fPairPosition.length = pair.getLength();
            fAnchor = fMatcher.getAnchor();

            fTextWidget.addPaintListener(this);
            fPaintPositionManager.managePosition(fPairPosition);
            handleDrawRequest(null);
        }
    }

    /*
     * @see org.eclipse.jface.text.IPainter#setPositionManager(org.eclipse.jface.text.IPaintPositionManager)
     */
    public void setPositionManager(IPaintPositionManager manager) {
        fPaintPositionManager = manager;
    }
}