/* * StructureMatcher.java - Abstract interface for bracket matching, etc. * :tabSize=4:indentSize=4:noTabs=false: * :folding=explicit:collapseFolds=1: * * Copyright (C) 2003 Slava Pestov * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.gjt.sp.jedit.textarea; //{{{ Imports import java.awt.*; import org.gjt.sp.jedit.TextUtilities; //}}} /** * An interface for matching parts of a source file's stucture. The default * implementation matches brackets. The XML plugin provides an implementation * for matching XML tags. * * @author Slava Pestov * @version $Id: StructureMatcher.java 23710 2014-11-01 23:35:36Z ezust $ * @since jEdit 4.2pre3 */ public interface StructureMatcher { //{{{ getMatch() method /** * Returns the element matching the one at the given text area's * caret position, or null. * @since jEdit 4.2pre3 */ Match getMatch(TextArea textArea); //}}} //{{{ selectMatch() method /** * Selects from the caret to the matching structure element (if there is * one, otherwise the behavior of this method is undefined). * @since jEdit 4.2pre3 */ void selectMatch(TextArea textArea); //}}} //{{{ BracketMatcher class static class BracketMatcher implements StructureMatcher { public Match getMatch(TextArea textArea) { int offset = textArea.getCaretPosition() - textArea.getLineStartOffset( textArea.getCaretLine()); if(offset != 0) { int bracketOffset = TextUtilities.findMatchingBracket( textArea.getBuffer(), textArea.getCaretLine(), offset - 1); if(bracketOffset != -1) { int bracketLine = textArea .getLineOfOffset( bracketOffset); return new Match(this, bracketLine, bracketOffset, bracketLine, bracketOffset + 1); } } return null; } public void selectMatch(TextArea textArea) { textArea.selectToMatchingBracket(); } } //}}} //{{{ Match class /** * A structure match, denoted by a start and end position. * @since jEdit 4.2pre3 */ public static class Match { public StructureMatcher matcher; public int startLine; public int start; public int endLine; public int end; public Match() {} public Match(StructureMatcher matcher) { this.matcher = matcher; } public Match(StructureMatcher matcher, int startLine, int start, int endLine, int end) { this(matcher); this.startLine = startLine; this.start = start; this.endLine = endLine; this.end = end; } } //}}} //{{{ Highlight class /** * Paints the structure match highlight. */ static class Highlight extends TextAreaExtension { Highlight(TextArea textArea) { this.textArea = textArea; } public void paintValidLine(Graphics2D gfx, int screenLine, int physicalLine, int start, int end, int y) { if(!textArea.getPainter().isStructureHighlightEnabled()) return; Match match = textArea.getStructureMatch(); if(match != null) { paintHighlight(gfx,screenLine, start,end,y,match); } } private int[] getOffsets(int screenLine, Match match) { int x1, x2; int matchStartLine = textArea.getScreenLineOfOffset( match.start); int matchEndLine = textArea.getScreenLineOfOffset( match.end); if(matchStartLine == screenLine) { x1 = match.start; } else { x1 = textArea.getScreenLineStartOffset( screenLine); } if(matchEndLine == screenLine) { x2 = match.end; } else { x2 = textArea.getScreenLineEndOffset( screenLine) - 1; } return new int[] { textArea.offsetToXY(x1).x, textArea.offsetToXY(x2).x }; } private void paintHighlight(Graphics gfx, int screenLine, int start, int end, int y, Match match) { if(!textArea.isStructureHighlightVisible()) return; if(match.start >= end || match.end < start) { return; } int matchStartLine = textArea.getScreenLineOfOffset( match.start); int matchEndLine = textArea.getScreenLineOfOffset( match.end); int height = Math.min( textArea.getPainter().getLineHeight(), textArea.getPainter().getFontHeight()); y += Math.max(textArea.getPainter().getLineExtraSpacing(), 0); int[] offsets = getOffsets(screenLine,match); int x1 = offsets[0]; int x2 = offsets[1]; gfx.setColor(textArea.getPainter().getStructureHighlightColor()); gfx.drawLine(x1,y,x1,y + height - 1); gfx.drawLine(x2,y,x2,y + height - 1); if(matchStartLine == screenLine || screenLine == 0) gfx.drawLine(x1,y,x2,y); else { offsets = getOffsets(screenLine - 1,match); int prevX1 = offsets[0]; int prevX2 = offsets[1]; gfx.drawLine(Math.min(x1,prevX1),y, Math.max(x1,prevX1),y); gfx.drawLine(Math.min(x2,prevX2),y, Math.max(x2,prevX2),y); } if(matchEndLine == screenLine) { gfx.drawLine(x1,y + height - 1, x2,y + height - 1); } } private TextArea textArea; } //}}} }