All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jface.text.source.MatchingCharacterPainter Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (c) 2000, 2016 IBM Corporation 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jface.text.source;


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.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;

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.ITextListener;
import org.eclipse.jface.text.ITextViewerExtension5;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextEvent;

/**
 * Highlights the peer character matching the character near the caret position, or a pair of peer
 * characters enclosing the caret position. This painter can be configured with an
 * {@link org.eclipse.jface.text.source.ICharacterPairMatcher} or an
 * {@link org.eclipse.jface.text.source.ICharacterPairMatcherExtension}.
 * 

* Clients instantiate and configure an 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; /** * Whether to highlight enclosing peer characters or not. * * @since 3.8 */ private boolean fHighlightEnclosingPeerCharacters; /** * Whether to highlight the character at caret location or not. * * @since 3.8 */ private boolean fHighlightCharacterAtCaretLocation; /** * Whether a character is present at caret location or not. * * @since 3.8 */ private boolean fCharacterPresentAtCaretLocation; /** * The previous selection, used to determine the need for computing enclosing brackets. * * @since 3.8 */ private IRegion fPreviousSelection; /** * Previous length of the document this painter is associated with. * * @since 3.8 */ private int fPreviousLengthOfDocument; /** * The text viewer change listener. * * @since 3.8 */ private TextListener fTextListener; /** * 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 the source viewer * @param matcher the character pair matcher */ public MatchingCharacterPainter(ISourceViewer sourceViewer, ICharacterPairMatcher matcher) { fSourceViewer= sourceViewer; fMatcher= matcher; fTextWidget= sourceViewer.getTextWidget(); } /** * Sets whether to highlight the character at caret location or not. * * @param highlightCharacterAtCaretLocation whether to highlight the character at caret location * or not * @since 3.8 */ public void setHighlightCharacterAtCaretLocation(boolean highlightCharacterAtCaretLocation) { handleDrawRequest(null); // see https://bugs.eclipse.org/372515 fHighlightCharacterAtCaretLocation= highlightCharacterAtCaretLocation; } /** * Sets whether to highlight enclosing peer characters or not. * * @param highlightEnclosingPeerCharcters whether to highlight enclosing peer characters or not * @since 3.8 */ public void setHighlightEnclosingPeerCharacters(boolean highlightEnclosingPeerCharcters) { fHighlightEnclosingPeerCharacters= highlightEnclosingPeerCharcters; installUninstallTextListener(highlightEnclosingPeerCharcters); } /** * Sets the color in which to highlight the match character. * * @param color the color */ public void setColor(Color color) { fColor= color; } @Override public void dispose() { if (fMatcher != null) { if (fMatcher instanceof ICharacterPairMatcherExtension && fTextListener != null) { installUninstallTextListener(false); } fMatcher.clear(); fMatcher= null; } fColor= null; fTextWidget= null; } @Override public void deactivate(boolean redraw) { if (fIsActive) { fIsActive= false; fTextWidget.removePaintListener(this); if (fPaintPositionManager != null) fPaintPositionManager.unmanagePosition(fPairPosition); if (redraw) handleDrawRequest(null); } fPreviousSelection= null; } @Override public void paintControl(PaintEvent event) { if (fTextWidget != null) handleDrawRequest(event.gc); } /** * Handles a redraw request. * * @param gc the GC to draw into or null to send a redraw request if necessary */ 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(); } if (fHighlightCharacterAtCaretLocation || (fHighlightEnclosingPeerCharacters && !fCharacterPresentAtCaretLocation)) { draw(gc, offset); draw(gc, offset + length - 1); } else { if (ICharacterPairMatcher.RIGHT == fAnchor) draw(gc, offset); else draw(gc, offset + length - 1); } } /** * Highlights the given widget region. * * @param gc the GC to draw into or null to send a redraw request * @param offset the offset of the widget region */ private void draw(GC gc, int offset) { int length = 1; if (gc != null) { gc.setForeground(fColor); Rectangle bounds= fTextWidget.getTextBounds(offset, offset + length - 1); // draw box around line segment gc.drawRectangle(bounds.x, bounds.y, bounds.width - 1, bounds.height - 1); // draw box around character area // int widgetBaseline= fTextWidget.getBaseline(); // FontMetrics fm= gc.getFontMetrics(); // int fontBaseline= fm.getAscent() + fm.getLeading(); // int fontBias= widgetBaseline - fontBaseline; // gc.drawRectangle(left.x, left.y + fontBias, right.x - left.x - 1, fm.getHeight() - 1); } else { fTextWidget.redrawRange(offset, length, true); } } /** * Returns the signed current selection. The length will be negative if the resulting selection * is right-to-left. *

* The selection offset is model based. *

* * @param sourceViewer the source viewer * @return a region denoting the current signed selection, for a resulting RtoL selections * length is < 0 * @since 3.8 */ private static final IRegion getSignedSelection(ISourceViewer sourceViewer) { Point viewerSelection= sourceViewer.getSelectedRange(); StyledText text= sourceViewer.getTextWidget(); Point selection= text.getSelectionRange(); if (text.getCaretOffset() == selection.x) { viewerSelection.x= viewerSelection.x + viewerSelection.y; viewerSelection.y= -viewerSelection.y; } return new Region(viewerSelection.x, viewerSelection.y); } @Override public void paint(int reason) { IDocument document= fSourceViewer.getDocument(); if (document == null) { deactivate(false); return; } IRegion selection= getSignedSelection(fSourceViewer); IRegion pair; boolean characterPresentAtCaretLocation; if (fMatcher instanceof ICharacterPairMatcherExtension) { ICharacterPairMatcherExtension matcher= (ICharacterPairMatcherExtension)fMatcher; pair= matcher.match(document, selection.getOffset(), selection.getLength()); characterPresentAtCaretLocation= (pair != null); if (pair == null && fHighlightEnclosingPeerCharacters) { int length= document.getLength(); boolean lengthChanged= length != fPreviousLengthOfDocument; fPreviousLengthOfDocument= length; if (reason != IPainter.CONFIGURATION && fSourceViewer.getDocument() == document && !lengthChanged && (selection.equals(fPreviousSelection) || fPreviousSelection == null)) { return; } if (reason == IPainter.TEXT_CHANGE) { fPreviousSelection= selection; return; } if (reason != IPainter.CONFIGURATION && !lengthChanged && fPreviousSelection != null && reason != IPainter.INTERNAL) { if (!matcher.isRecomputationOfEnclosingPairRequired(document, selection, fPreviousSelection)) { if (fCharacterPresentAtCaretLocation && !fHighlightCharacterAtCaretLocation) { fCharacterPresentAtCaretLocation= false; handleDrawRequest(null); } fPreviousSelection= selection; return; } } pair= matcher.findEnclosingPeerCharacters(document, selection.getOffset(), selection.getLength()); } } else { if (Math.abs(selection.getLength()) > 0) { deactivate(true); return; } pair= fMatcher.match(document, selection.getOffset()); characterPresentAtCaretLocation= (pair != null); } fPreviousSelection= selection; 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 || characterPresentAtCaretLocation != fCharacterPresentAtCaretLocation) { // 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(); fCharacterPresentAtCaretLocation= characterPresentAtCaretLocation; // apply new highlighting handleDrawRequest(null); } } else { fIsActive= true; fPairPosition.isDeleted= false; fPairPosition.offset= pair.getOffset(); fPairPosition.length= pair.getLength(); fAnchor= fMatcher.getAnchor(); fCharacterPresentAtCaretLocation= characterPresentAtCaretLocation; fTextWidget.addPaintListener(this); fPaintPositionManager.managePosition(fPairPosition); handleDrawRequest(null); } } @Override public void setPositionManager(IPaintPositionManager manager) { fPaintPositionManager= manager; } /** * Installs or uninstalls the text listener depending on the boolean parameter. * * @param install true to install the text listener, false to uninstall * * @since 3.8 */ private void installUninstallTextListener(boolean install) { if (!(fMatcher instanceof ICharacterPairMatcherExtension)) return; if (install) { fTextListener= new TextListener(); fSourceViewer.addTextListener(fTextListener); } else { if (fTextListener != null) { fSourceViewer.removeTextListener(fTextListener); fTextListener= null; } } } /** * Listens to document changes and if required by those document changes causes a re-computation * of matching characters. * * @since 3.8 */ private class TextListener implements ITextListener { /** * @see org.eclipse.jface.text.ITextListener#textChanged(org.eclipse.jface.text.TextEvent) */ @Override public void textChanged(TextEvent event) { if (!fHighlightEnclosingPeerCharacters || !(fMatcher instanceof ICharacterPairMatcherExtension)) return; if (!event.getViewerRedrawState()) return; String text= event.getText(); String replacedText= event.getReplacedText(); ICharacterPairMatcherExtension matcher= (ICharacterPairMatcherExtension)fMatcher; if (searchForCharacters(text, matcher) || searchForCharacters(replacedText, matcher)) paint(IPainter.INTERNAL); } /** * Searches for matched characters in the given string. * * @param text the string to search * @param matcher the pair matcher * @return true if a matched character is found, false otherwise */ private boolean searchForCharacters(String text, ICharacterPairMatcherExtension matcher) { if (text == null) return false; for (int i= 0; i < text.length(); i++) { if (matcher.isMatchedChar(text.charAt(i))) { return true; } } return false; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy