001: /*
002: Copyright 2001 Nicholas Allen (nallen@freenet.co.uk)
003: This file is part of JavaCVS.
004: JavaCVS is free software; you can redistribute it and/or modify
005: it under the terms of the GNU General Public License as published by
006: the Free Software Foundation; either version 2 of the License, or
007: (at your option) any later version.
008: JavaCVS is distributed in the hope that it will be useful,
009: but WITHOUT ANY WARRANTY; without even the implied warranty of
010: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
011: GNU General Public License for more details.
012: You should have received a copy of the GNU General Public License
013: along with JavaCVS; if not, write to the Free Software
014: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
015: */
016: package allensoft.diff;
017:
018: import gruntspud.GruntspudContext;
019: import gruntspud.standalone.JDK13GruntspudHost;
020: import gruntspud.style.TextStyle;
021:
022: import java.awt.Color;
023: import java.awt.Dimension;
024: import java.awt.Font;
025: import java.awt.FontMetrics;
026: import java.awt.Graphics;
027: import java.awt.Rectangle;
028: import java.util.ArrayList;
029:
030: import javax.swing.JComponent;
031: import javax.swing.JPanel;
032: import javax.swing.JViewport;
033: import javax.swing.Scrollable;
034: import javax.swing.SwingConstants;
035:
036: /* A component that displays differences with colored text and background. */
037: public class DiffDisplay extends JPanel implements Scrollable {
038: private java.util.List m_Lines = new ArrayList(50);
039: private Font m_DisplayFont;
040: private FontMetrics m_FontMetrics = null;
041: private int m_nTabSize;
042: private TextStyle m_NoStyle;
043: private TextStyle m_InsertStyle;
044: private TextStyle m_DeleteStyle;
045: private TextStyle m_ChangeStyle;
046: private TextStyle m_NonExistantStyle;
047: private JComponent m_RowHeader = null;
048: private char[] m_Chars = new char[1];
049: private int m_nMarginWidth;
050:
051: /* Creates a new DiffDisplay using the supplied font for the text and tab size for
052: the tabs. Lines of text to be displayed should be added with the addLine method. */
053: public DiffDisplay(Font font, int nTabSize, GruntspudContext context) {
054: setPreferredSize(new Dimension(100, 100));
055: m_DisplayFont = font;
056: m_nTabSize = nTabSize;
057:
058: setNonExistantStyle(context.getTextStyleModel().getStyle(
059: JDK13GruntspudHost.OPTIONS_STYLE_DIFF_NON_EXISTANT));
060: setStyle(DiffType.NONE, context.getTextStyleModel().getStyle(
061: JDK13GruntspudHost.OPTIONS_STYLE_DIFF_IDENTICAL));
062: setStyle(
063: DiffType.INSERTION,
064: context
065: .getTextStyleModel()
066: .getStyle(
067: JDK13GruntspudHost.OPTIONS_STYLE_DIFF_INSERTION));
068: setStyle(DiffType.DELETION, context.getTextStyleModel()
069: .getStyle(
070: JDK13GruntspudHost.OPTIONS_STYLE_DIFF_DELETION));
071: setStyle(DiffType.CHANGE, context.getTextStyleModel().getStyle(
072: JDK13GruntspudHost.OPTIONS_STYLE_DIFF_CHANGE));
073:
074: }
075:
076: /* Creates a DiffDisplay using a monospaced font and tab size for
077: the tabs. Lines of text to be displayed should be added with the addLine method. */
078: public DiffDisplay(int nTabSize, GruntspudContext context) {
079: this (new Font("Monospaced", Font.PLAIN, 12), nTabSize, context);
080: }
081:
082: /* Creates a DiffDisplay using a monospaced font and a tab size of 4 for
083: the tabs. Lines of text to be displayed should be added with the addLine method. */
084: public DiffDisplay(GruntspudContext context) {
085: this (4, context);
086: }
087:
088: /**
089: * DOCUMENT ME!
090: *
091: * @return DOCUMENT ME!
092: */
093: public Dimension getPreferredScrollableViewportSize() {
094: return getPreferredSize();
095: }
096:
097: /**
098: * DOCUMENT ME!
099: *
100: * @param visibleRect DOCUMENT ME!
101: * @param orientation DOCUMENT ME!
102: * @param direction DOCUMENT ME!
103: *
104: * @return DOCUMENT ME!
105: */
106: public int getScrollableUnitIncrement(Rectangle visibleRect,
107: int orientation, int direction) {
108: if (m_FontMetrics != null) {
109: if (orientation == SwingConstants.VERTICAL)
110: return m_FontMetrics.getHeight();
111:
112: return m_FontMetrics.charWidth(' ');
113: }
114:
115: return 10;
116: }
117:
118: /**
119: * DOCUMENT ME!
120: *
121: * @param visibleRect DOCUMENT ME!
122: * @param orientation DOCUMENT ME!
123: * @param direction DOCUMENT ME!
124: *
125: * @return DOCUMENT ME!
126: */
127: public int getScrollableBlockIncrement(Rectangle visibleRect,
128: int orientation, int direction) {
129: if (orientation == SwingConstants.VERTICAL)
130: return visibleRect.height;
131:
132: return visibleRect.width;
133: }
134:
135: /**
136: * DOCUMENT ME!
137: *
138: * @return DOCUMENT ME!
139: */
140: public boolean getScrollableTracksViewportWidth() {
141: return false;
142: }
143:
144: /**
145: * DOCUMENT ME!
146: *
147: * @return DOCUMENT ME!
148: */
149: public boolean getScrollableTracksViewportHeight() {
150: return false;
151: }
152:
153: /** Adds a line to be displayed. This method should only be called
154: before displaying this component. It should not be called after the
155: component has been displayed. */
156: public void addLine(int nLineNum, DiffType type, String sLine) {
157: m_Lines.add(new Line(nLineNum, type, sLine));
158: }
159:
160: /**
161: * DOCUMENT ME!
162: */
163: public void addNonExistantLine() {
164: m_Lines.add(new Line(-1, DiffType.NONE, ""));
165: }
166:
167: /** Gets the font used to display the text. */
168: public Font getDisplayFont() {
169: return m_DisplayFont;
170: }
171:
172: /** Sets the font used to display the text. */
173: public synchronized void setDisplayFont(Font font) {
174: m_DisplayFont = font;
175: m_FontMetrics = null;
176: calculatePreferredSize();
177: }
178:
179: /**
180: * DOCUMENT ME!
181: *
182: * @return DOCUMENT ME!
183: */
184: public int getTabSize() {
185: return m_nTabSize;
186: }
187:
188: /**
189: * DOCUMENT ME!
190: *
191: * @param nTabSize DOCUMENT ME!
192: */
193: public void setTabSize(int nTabSize) {
194: m_nTabSize = nTabSize;
195: calculatePreferredSize();
196: }
197:
198: /**
199: * DOCUMENT ME!
200: *
201: * @param type DOCUMENT ME!
202: *
203: * @return DOCUMENT ME!
204: */
205: public TextStyle getStyle(DiffType type) {
206: TextStyle style = m_NoStyle;
207:
208: if (type == DiffType.NONE)
209: ;
210:
211: else if (type == DiffType.INSERTION)
212: style = m_InsertStyle;
213:
214: else if (type == DiffType.DELETION)
215: style = m_DeleteStyle;
216:
217: else
218: style = m_ChangeStyle;
219:
220: return style;
221: }
222:
223: /**
224: * DOCUMENT ME!
225: *
226: * @param type DOCUMENT ME!
227: * @param style DOCUMENT ME!
228: */
229: public synchronized void setStyle(DiffType type, TextStyle style) {
230: if (type == DiffType.NONE) {
231: m_NoStyle = style;
232: setBackground(style.getBackground());
233: } else if (type == DiffType.INSERTION)
234: m_InsertStyle = style;
235:
236: else if (type == DiffType.DELETION)
237: m_DeleteStyle = style;
238:
239: else
240: m_ChangeStyle = style;
241:
242: repaint();
243: }
244:
245: /**
246: * DOCUMENT ME!
247: *
248: * @return DOCUMENT ME!
249: */
250: public TextStyle getNonExistantStyle() {
251: return m_NonExistantStyle;
252: }
253:
254: /**
255: * DOCUMENT ME!
256: *
257: * @param style DOCUMENT ME!
258: */
259: public synchronized void setNonExistantStyle(TextStyle style) {
260: m_NonExistantStyle = style;
261: }
262:
263: protected synchronized void paintComponent(Graphics g) {
264: super .paintComponent(g);
265:
266: // We can only calculate the size of this component from a FontMetrics which
267: // can only be obtained from a Graphics object so we need to compute our preferred
268: // size in this method.
269: if (m_FontMetrics == null) {
270: m_FontMetrics = g.getFontMetrics(m_DisplayFont);
271: calculatePreferredSize();
272:
273: return;
274: }
275:
276: g.setFont(m_FontMetrics.getFont());
277:
278: // Paint lines that need painting
279: Rectangle clipBounds = g.getClipBounds();
280: paintLines(g, clipBounds.y / m_FontMetrics.getHeight(),
281: (clipBounds.y + clipBounds.height)
282: / m_FontMetrics.getHeight());
283:
284: // Draw left margin line
285: g.setColor(Color.gray);
286: g.drawLine(m_nMarginWidth, clipBounds.y, m_nMarginWidth,
287: clipBounds.y + clipBounds.height);
288:
289: // Draw right margin line
290: int x = getPreferredSize().width - 1;
291: g
292: .drawLine(x, clipBounds.y, x, clipBounds.y
293: + clipBounds.height);
294:
295: // Draw line showing end of file
296: int y = (m_Lines.size() * m_FontMetrics.getHeight()) - 1;
297: g.drawLine(0, y, getSize().width, y);
298:
299: // Paint to the end of the viewport
300: if (getParent() instanceof JViewport) {
301: Dimension s = ((JViewport) getParent()).getSize();
302: y++;
303: //g.setColor(getBackground());
304: g.setColor(Color.red);
305: g.fillRect(0, y, s.width, s.height - y);
306: }
307: }
308:
309: private void calculatePreferredSize() {
310: int nWidth = 100;
311:
312: if (m_FontMetrics == null)
313: return;
314:
315: m_nMarginWidth = (String.valueOf(m_Lines.size()).length() + 1)
316: * m_FontMetrics.charWidth('9');
317:
318: for (int i = 0; i < m_Lines.size(); i++) {
319: Line line = (Line) m_Lines.get(i);
320:
321: // Calculate width of line
322: String sLine = line.m_sLine;
323: int length = sLine.length();
324: int x = 0;
325: int nSpaceWidth = m_FontMetrics.charWidth(' ');
326:
327: for (int nChar = 0, nActual = 0; nChar < length; nChar++) {
328: char c = sLine.charAt(nChar);
329:
330: if (c == '\t') {
331: int nNumSpaces = (((nActual / m_nTabSize) + 1) * m_nTabSize)
332: - nActual;
333: nActual += nNumSpaces;
334: x += (nSpaceWidth * nNumSpaces);
335:
336: continue;
337: } else if (c == ' ') {
338: nActual++;
339: x += nSpaceWidth;
340:
341: continue;
342: } else {
343: nActual++;
344: x += m_FontMetrics.charWidth(c);
345: }
346: }
347:
348: nWidth = Math.max(nWidth, x);
349: }
350:
351: setPreferredSize(new Dimension(m_nMarginWidth + nWidth,
352: m_FontMetrics.getHeight() * m_Lines.size()));
353: revalidate();
354: repaint();
355: }
356:
357: private void paintLines(Graphics g, int nStart, int nEnd) {
358: for (int i = nStart; (i <= nEnd) && (i < m_Lines.size()); i++)
359: paintLine(g, i);
360: }
361:
362: public int lineYPos(int lineNo) {
363: if (lineNo < 0) {
364: lineNo = 0;
365: } else if (lineNo >= m_Lines.size()) {
366: lineNo = m_Lines.size() - 1;
367: }
368: return lineNo * m_FontMetrics.getHeight();
369: }
370:
371: private void paintLine(Graphics g, int nLine) {
372: Line line = (Line) m_Lines.get(nLine);
373: int nWidth = getSize().width;
374: TextStyle style = (line.m_nLineNum == -1) ? m_NonExistantStyle
375: : getStyle(line.m_Type);
376: int y = lineYPos(nLine);
377:
378: g.setColor(style.getBackground());
379: g.fillRect(0, y, nWidth, m_FontMetrics.getHeight());
380:
381: y += m_FontMetrics.getAscent();
382:
383: // Draw line number in margin
384: if (line.m_nLineNum != -1) {
385: if (line.m_Type == DiffType.NONE)
386: g.setColor(Color.red);
387:
388: else
389: g.setColor(Color.black);
390:
391: g.drawString(String.valueOf(line.m_nLineNum), 0, y);
392: }
393:
394: // Draw characters
395: g.setColor(style.getForeground());
396:
397: String sLine = line.m_sLine;
398: int length = sLine.length();
399: int x = m_nMarginWidth;
400: int nSpaceWidth = m_FontMetrics.charWidth(' ');
401: Rectangle clipBounds = g.getClipBounds();
402: int nStartClipX = clipBounds.x;
403: int nEndClipX = clipBounds.x + clipBounds.width;
404:
405: for (int nChar = 0, nActual = 0; nChar < length; nChar++) {
406: char c = sLine.charAt(nChar);
407:
408: if (c == '\t') {
409: int nNumSpaces = (((nActual / m_nTabSize) + 1) * m_nTabSize)
410: - nActual;
411: nActual += nNumSpaces;
412: x += (nSpaceWidth * nNumSpaces);
413:
414: continue;
415: } else if (c == ' ') {
416: nActual++;
417: x += nSpaceWidth;
418:
419: continue;
420: } else {
421: int nCharWidth = m_FontMetrics.charWidth(c);
422: int x2 = x + nCharWidth;
423:
424: // Only draw char if needed
425: if (!((x2 < nStartClipX) || (x > nEndClipX))) {
426: m_Chars[0] = c;
427: g.drawChars(m_Chars, 0, 1, x, y);
428: }
429:
430: if (x > nEndClipX)
431: break;
432:
433: nActual++;
434: x += nCharWidth;
435: }
436: }
437: }
438:
439: java.util.List getLines() {
440: return m_Lines;
441: }
442:
443: }
|