001: /*
002: * StructureMatcher.java - Abstract interface for bracket matching, etc.
003: * :tabSize=8:indentSize=8:noTabs=false:
004: * :folding=explicit:collapseFolds=1:
005: *
006: * Copyright (C) 2003 Slava Pestov
007: *
008: * This program is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU General Public License
010: * as published by the Free Software Foundation; either version 2
011: * of the License, or any later version.
012: *
013: * This program is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU General Public License for more details.
017: *
018: * You should have received a copy of the GNU General Public License
019: * along with this program; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
021: */
022:
023: package org.gjt.sp.jedit.textarea;
024:
025: //{{{ Imports
026: import java.awt.*;
027: import org.gjt.sp.jedit.TextUtilities;
028:
029: //}}}
030:
031: /**
032: * An interface for matching parts of a source file's stucture. The default
033: * implementation matches brackets. The XML plugin provides an implementation
034: * for matching XML tags.
035: *
036: * @author Slava Pestov
037: * @version $Id: StructureMatcher.java 7156 2006-10-02 21:33:17Z kpouer $
038: * @since jEdit 4.2pre3
039: */
040: public interface StructureMatcher {
041: //{{{ getMatch() method
042: /**
043: * Returns the element matching the one at the given text area's
044: * caret position, or null.
045: * @since jEdit 4.2pre3
046: */
047: Match getMatch(TextArea textArea);
048:
049: //}}}
050:
051: //{{{ selectMatch() method
052: /**
053: * Selects from the caret to the matching structure element (if there is
054: * one, otherwise the behavior of this method is undefined).
055: * @since jEdit 4.2pre3
056: */
057: void selectMatch(TextArea textArea);
058:
059: //}}}
060:
061: //{{{ BracketMatcher class
062: static class BracketMatcher implements StructureMatcher {
063: public Match getMatch(TextArea textArea) {
064: int offset = textArea.getCaretPosition()
065: - textArea.getLineStartOffset(textArea
066: .getCaretLine());
067:
068: if (offset != 0) {
069: int bracketOffset = TextUtilities.findMatchingBracket(
070: textArea.getBuffer(), textArea.getCaretLine(),
071: offset - 1);
072: if (bracketOffset != -1) {
073: int bracketLine = textArea
074: .getLineOfOffset(bracketOffset);
075: return new Match(this , bracketLine, bracketOffset,
076: bracketLine, bracketOffset + 1);
077: }
078: }
079:
080: return null;
081: }
082:
083: public void selectMatch(TextArea textArea) {
084: textArea.selectToMatchingBracket();
085: }
086: } //}}}
087:
088: //{{{ Match class
089: /**
090: * A structure match, denoted by a start and end position.
091: * @since jEdit 4.2pre3
092: */
093: public static class Match {
094: public StructureMatcher matcher;
095: public int startLine;
096: public int start;
097: public int endLine;
098: public int end;
099:
100: public Match() {
101: }
102:
103: public Match(StructureMatcher matcher) {
104: this .matcher = matcher;
105: }
106:
107: public Match(StructureMatcher matcher, int startLine,
108: int start, int endLine, int end) {
109: this (matcher);
110: this .startLine = startLine;
111: this .start = start;
112: this .endLine = endLine;
113: this .end = end;
114: }
115: } //}}}
116:
117: //{{{ Highlight class
118: /**
119: * Paints the structure match highlight.
120: */
121: static class Highlight extends TextAreaExtension {
122: Highlight(TextArea textArea) {
123: this .textArea = textArea;
124: }
125:
126: public void paintValidLine(Graphics2D gfx, int screenLine,
127: int physicalLine, int start, int end, int y) {
128: if (!textArea.getPainter().isStructureHighlightEnabled())
129: return;
130:
131: Match match = textArea.getStructureMatch();
132: if (match != null) {
133: paintHighlight(gfx, screenLine, start, end, y, match);
134: }
135: }
136:
137: private int[] getOffsets(int screenLine, Match match) {
138: int x1, x2;
139:
140: int matchStartLine = textArea
141: .getScreenLineOfOffset(match.start);
142: int matchEndLine = textArea
143: .getScreenLineOfOffset(match.end);
144:
145: if (matchStartLine == screenLine) {
146: x1 = match.start;
147: } else {
148: x1 = textArea.getScreenLineStartOffset(screenLine);
149: }
150:
151: if (matchEndLine == screenLine) {
152: x2 = match.end;
153: } else {
154: x2 = textArea.getScreenLineEndOffset(screenLine) - 1;
155: }
156:
157: return new int[] { textArea.offsetToXY(x1).x,
158: textArea.offsetToXY(x2).x };
159: }
160:
161: private void paintHighlight(Graphics gfx, int screenLine,
162: int start, int end, int y, Match match) {
163: if (!textArea.isStructureHighlightVisible())
164: return;
165:
166: if (match.start >= end || match.end < start) {
167: return;
168: }
169:
170: int matchStartLine = textArea
171: .getScreenLineOfOffset(match.start);
172: int matchEndLine = textArea
173: .getScreenLineOfOffset(match.end);
174:
175: FontMetrics fm = textArea.getPainter().getFontMetrics();
176: int height = fm.getHeight();
177:
178: int[] offsets = getOffsets(screenLine, match);
179: int x1 = offsets[0];
180: int x2 = offsets[1];
181:
182: gfx.setColor(textArea.getPainter()
183: .getStructureHighlightColor());
184:
185: gfx.drawLine(x1, y, x1, y + height - 1);
186: gfx.drawLine(x2, y, x2, y + height - 1);
187:
188: if (matchStartLine == screenLine || screenLine == 0)
189: gfx.drawLine(x1, y, x2, y);
190: else {
191: offsets = getOffsets(screenLine - 1, match);
192: int prevX1 = offsets[0];
193: int prevX2 = offsets[1];
194:
195: gfx.drawLine(Math.min(x1, prevX1), y, Math.max(x1,
196: prevX1), y);
197: gfx.drawLine(Math.min(x2, prevX2), y, Math.max(x2,
198: prevX2), y);
199: }
200:
201: if (matchEndLine == screenLine) {
202: gfx.drawLine(x1, y + height - 1, x2, y + height - 1);
203: }
204: }
205:
206: private TextArea textArea;
207: } //}}}
208: }
|