001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.diff.builtin.visualizer.editable;
042:
043: import org.netbeans.editor.EditorUI;
044: import org.netbeans.editor.Utilities;
045: import org.netbeans.editor.BaseTextUI;
046: import org.netbeans.api.editor.fold.FoldHierarchy;
047: import org.netbeans.api.diff.Difference;
048: import org.openide.ErrorManager;
049: import org.openide.util.RequestProcessor;
050:
051: import javax.swing.*;
052: import javax.swing.text.*;
053: import java.awt.*;
054: import java.beans.PropertyChangeListener;
055: import java.beans.PropertyChangeEvent;
056:
057: /**
058: * Editor pane with added decorations (diff lines).
059: *
060: * @author Maros Sandor
061: */
062: class DecoratedEditorPane extends JEditorPane implements
063: PropertyChangeListener {
064:
065: private Difference[] currentDiff;
066: private DiffContentPanel master;
067:
068: private final RequestProcessor.Task repaintTask;
069:
070: private int fontHeight;
071: private int charWidth;
072:
073: public DecoratedEditorPane(DiffContentPanel master) {
074: repaintTask = RequestProcessor.getDefault().create(
075: new RepaintPaneTask());
076: setBorder(null);
077: this .master = master;
078: master.getMaster().addPropertyChangeListener(this );
079: }
080:
081: public boolean isFirst() {
082: return master.isFirst();
083: }
084:
085: public DiffContentPanel getMaster() {
086: return master;
087: }
088:
089: void setDifferences(Difference[] diff) {
090: currentDiff = diff;
091: repaint();
092: }
093:
094: public void setFont(Font font) {
095: super .setFont(font);
096: setFontHeightWidth(getFont());
097: }
098:
099: private void setFontHeightWidth(Font font) {
100: FontMetrics metrics = getFontMetrics(font);
101: fontHeight = metrics.getHeight();
102: charWidth = metrics.charWidth('m');
103: }
104:
105: public int getScrollableUnitIncrement(Rectangle visibleRect,
106: int orientation, int direction) {
107: switch (orientation) {
108: case SwingConstants.VERTICAL:
109: return fontHeight;
110: case SwingConstants.HORIZONTAL:
111: return charWidth;
112: default:
113: throw new IllegalArgumentException("Invalid orientation: "
114: + orientation); // discrimination
115: }
116: }
117:
118: protected void paintComponent(Graphics gr) {
119: super .paintComponent(gr);
120: if (currentDiff == null)
121: return;
122:
123: EditorUI editorUI = org.netbeans.editor.Utilities
124: .getEditorUI(this );
125:
126: Graphics2D g = (Graphics2D) gr.create();
127: Rectangle clip = g.getClipBounds();
128: Stroke cs = g.getStroke();
129: // compensate for cursor drawing, it is needed for catching a difference on the cursor line
130: clip.y -= 1;
131: clip.height += 1;
132:
133: FoldHierarchy foldHierarchy = FoldHierarchy.get(editorUI
134: .getComponent());
135: JTextComponent component = editorUI.getComponent();
136: if (component == null)
137: return;
138: View rootView = Utilities.getDocumentView(component);
139: if (rootView == null)
140: return;
141: BaseTextUI textUI = (BaseTextUI) component.getUI();
142:
143: AbstractDocument doc = (AbstractDocument) component
144: .getDocument();
145: doc.readLock();
146: try {
147: foldHierarchy.lock();
148: try {
149: int startPos = textUI.getPosFromY(clip.y);
150: int startViewIndex = rootView.getViewIndex(startPos,
151: Position.Bias.Forward);
152: int rootViewCount = rootView.getViewCount();
153:
154: if (startViewIndex >= 0
155: && startViewIndex < rootViewCount) {
156: // find the nearest visible line with an annotation
157: Rectangle rec = textUI.modelToView(component,
158: rootView.getView(startViewIndex)
159: .getStartOffset());
160: int y = (rec == null) ? 0 : rec.y;
161:
162: int clipEndY = clip.y + clip.height;
163: Element rootElem = textUI.getRootView(component)
164: .getElement();
165:
166: View view = rootView.getView(startViewIndex);
167: int line = rootElem.getElementIndex(view
168: .getStartOffset());
169: line++; // make it 1-based
170:
171: int curDif = master.getMaster()
172: .getCurrentDifference();
173:
174: g.setColor(master.getMaster().getColorLines());
175: if (master.isFirst()) {
176: for (int i = startViewIndex; i < rootViewCount; i++) {
177: view = rootView.getView(i);
178: line = rootElem.getElementIndex(view
179: .getStartOffset());
180: line++; // make it 1-based
181: Difference ad = EditableDiffView
182: .getFirstDifference(currentDiff,
183: line);
184: if (ad != null) {
185: // TODO: can cause AIOOBE, synchronize "currentDiff" and "curDif" variables
186: g
187: .setStroke(curDif >= 0
188: && curDif < currentDiff.length
189: && currentDiff[curDif] == ad ? master
190: .getMaster()
191: .getBoldStroke()
192: : cs);
193: int yy = y + editorUI.getLineHeight();
194: if (ad.getType() == Difference.ADD) {
195: g.drawLine(0, yy, getWidth(), yy);
196: ad = null;
197: } else {
198: if (ad.getFirstStart() == line) {
199: g.drawLine(0, y, getWidth(), y);
200: }
201: if (ad.getFirstEnd() == line) {
202: g.drawLine(0, yy, getWidth(),
203: yy);
204: }
205: }
206: }
207: y += editorUI.getLineHeight();
208: if (y >= clipEndY) {
209: break;
210: }
211: }
212: } else {
213: for (int i = startViewIndex; i < rootViewCount; i++) {
214: view = rootView.getView(i);
215: line = rootElem.getElementIndex(view
216: .getStartOffset());
217: line++; // make it 1-based
218: Difference ad = EditableDiffView
219: .getSecondDifference(currentDiff,
220: line);
221: if (ad != null) {
222: // TODO: can cause AIOOBE, synchronize "currentDiff" and "curDif" variables
223: g
224: .setStroke(curDif >= 0
225: && curDif < currentDiff.length
226: && currentDiff[curDif] == ad ? master
227: .getMaster()
228: .getBoldStroke()
229: : cs);
230: int yy = y + editorUI.getLineHeight();
231: if (ad.getType() == Difference.DELETE) {
232: g.drawLine(0, yy, getWidth(), yy);
233: ad = null;
234: } else {
235: if (ad.getSecondStart() == line) {
236: g.drawLine(0, y, getWidth(), y);
237: }
238: if (ad.getSecondEnd() == line) {
239: g.drawLine(0, yy, getWidth(),
240: yy);
241: }
242: }
243: }
244: y += editorUI.getLineHeight();
245: if (y >= clipEndY) {
246: break;
247: }
248: }
249: }
250: }
251: } finally {
252: foldHierarchy.unlock();
253: }
254: } catch (BadLocationException ble) {
255: ErrorManager.getDefault().notify(ble);
256: } finally {
257: doc.readUnlock();
258: }
259: }
260:
261: public void propertyChange(PropertyChangeEvent evt) {
262: repaintTask.schedule(150);
263: }
264:
265: private class RepaintPaneTask implements Runnable {
266: public void run() {
267: SwingUtilities.invokeLater(new Runnable() {
268: public void run() {
269: repaint();
270: }
271: });
272: }
273: }
274: }
|