001: /*
002: * Copyright 2001 Nicholas Allen (nallen@freenet.co.uk) This file is part of
003: * JavaCVS. JavaCVS is free software; you can redistribute it and/or modify it
004: * under the terms of the GNU General Public License as published by the Free
005: * Software Foundation; either version 2 of the License, or (at your option)
006: * any later version. JavaCVS is distributed in the hope that it will be
007: * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
008: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
009: * Public License for more details. You should have received a copy of the GNU
010: * General Public License along with JavaCVS; if not, write to the Free
011: * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
012: * USA
013: */
014: package allensoft.diff;
015:
016: import gruntspud.GruntspudContext;
017: import gruntspud.actions.AbstractNextAction;
018: import gruntspud.actions.AbstractPreviousAction;
019: import gruntspud.style.TextStyle;
020: import gruntspud.ui.UIUtil;
021:
022: import java.awt.BorderLayout;
023: import java.awt.Color;
024: import java.awt.Font;
025: import java.awt.GridBagConstraints;
026: import java.awt.GridBagLayout;
027: import java.awt.Insets;
028: import java.awt.Point;
029: import java.awt.Rectangle;
030: import java.awt.event.ActionEvent;
031: import java.io.IOException;
032: import java.io.Reader;
033:
034: import javax.swing.Action;
035: import javax.swing.BorderFactory;
036: import javax.swing.JButton;
037: import javax.swing.JCheckBox;
038: import javax.swing.JLabel;
039: import javax.swing.JPanel;
040: import javax.swing.JScrollPane;
041: import javax.swing.JSplitPane;
042: import javax.swing.ScrollPaneConstants;
043: import javax.swing.event.ChangeEvent;
044: import javax.swing.event.ChangeListener;
045:
046: //import allensoft.util.*;
047: /** Views differences side by side in a JSplitPane. */
048: public class DiffViewer extends JPanel {
049: private static Rectangle g_WindowBounds;
050: private JSplitPane m_SplitPane;
051: private DiffDisplay m_Left;
052: private DiffDisplay m_Right;
053: private DiffNavigator m_Navi;
054: private JPanel m_NextPrevPanel;
055: private JScrollPane m_LeftScroll;
056: private Action nextAction, previousAction;
057:
058: /**
059: * Creates a new DiffViewer object.
060: *
061: * @param sLeftTitle
062: * DOCUMENT ME!
063: * @param sRightTitle
064: * DOCUMENT ME!
065: * @param in
066: * DOCUMENT ME!
067: * @param parser
068: * DOCUMENT ME!
069: * @param bInvert
070: * DOCUMENT ME!
071: *
072: * @throws DiffException
073: * DOCUMENT ME!
074: * @throws IOException
075: * DOCUMENT ME!
076: */
077: public DiffViewer(String sLeftTitle, String sRightTitle, Reader in,
078: DiffParser parser, boolean bInvert, GruntspudContext context)
079: throws DiffException, IOException {
080: super (new BorderLayout());
081: m_Left = createDiffDisplay(context);
082: m_Right = createDiffDisplay(context);
083: if (bInvert)
084: parser = new ReverseDiffParser(parser);
085: // Initialize the diff displays with the appropriate lines
086: DiffDisplayInitializer initializer = new DiffDisplayInitializer(
087: in, parser, m_Left, m_Right, bInvert);
088: initializer.run();
089: m_LeftScroll = new JScrollPane(m_Left,
090: ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
091: ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS) {
092: public boolean isFocusTraversable() {
093: return true;
094: }
095: };
096: m_LeftScroll.setBorder(BorderFactory
097: .createLineBorder(Color.black));
098: final JScrollPane rightScroll = new JScrollPane(m_Right,
099: ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
100: ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS) {
101: public boolean isFocusTraversable() {
102: return true;
103: }
104: };
105: rightScroll.setBorder(BorderFactory
106: .createLineBorder(Color.black));
107: final JCheckBox synchronizeScrollBars = new JCheckBox(
108: "Synchronize scroll bars", true);
109: final JButton previousButton = UIUtil.createButton(
110: previousAction = new PreviousAction(), true, false);
111: final JButton nextButton = UIUtil.createButton(
112: nextAction = new NextAction(), true, false);
113: Color background = m_Left.getStyle(DiffType.NONE)
114: .getBackground();
115: m_LeftScroll.getViewport().setBackground(background);
116: rightScroll.getViewport().setBackground(background);
117: JPanel leftPanel = new JPanel(new BorderLayout());
118: leftPanel.add(new JLabel(sLeftTitle), BorderLayout.NORTH);
119: leftPanel.add(m_LeftScroll, BorderLayout.CENTER);
120: m_Navi = new DiffNavigator(this , m_Left.getLines(), m_Right
121: .getLines(), context);
122: m_Navi.setBorder(BorderFactory.createLineBorder(Color.black));
123: leftPanel.add(m_Navi, BorderLayout.EAST);
124: JPanel rightPanel = new JPanel(new BorderLayout());
125: rightPanel.add(new JLabel(sRightTitle), BorderLayout.NORTH);
126: rightPanel.add(rightScroll, BorderLayout.CENTER);
127: m_SplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
128: false, leftPanel, rightPanel);
129: m_SplitPane.setOneTouchExpandable(true);
130: m_SplitPane.setDividerSize(7);
131: // This method only exists in 1.3 so we try and catch the
132: // NoSuchMethodError in case
133: // it is run on 1.2.X.
134: try {
135: m_SplitPane.setResizeWeight(0.5);
136: } catch (NoSuchMethodError e) {
137: }
138: add(m_SplitPane, BorderLayout.CENTER);
139: add(createNextPrevPanel(synchronizeScrollBars, previousButton,
140: nextButton), BorderLayout.SOUTH);
141: // Add listeners to synchronize scroll bars if enabled.
142: m_LeftScroll.getViewport().addChangeListener(
143: new ChangeListener() {
144: public void stateChanged(ChangeEvent e) {
145: if (synchronizeScrollBars.isSelected()) {
146: Point p = m_LeftScroll.getViewport()
147: .getViewPosition();
148: rightScroll.getViewport()
149: .setViewPosition(p);
150: }
151: }
152: });
153: rightScroll.getViewport().addChangeListener(
154: new ChangeListener() {
155: public void stateChanged(ChangeEvent e) {
156: if (synchronizeScrollBars.isSelected()) {
157: Point p = rightScroll.getViewport()
158: .getViewPosition();
159: m_LeftScroll.getViewport().setViewPosition(
160: p);
161: }
162: }
163: });
164: setAvailableActions();
165: }
166:
167: /**
168: * Creates a new DiffViewer object.
169: *
170: * @param sLeftTitle
171: * DOCUMENT ME!
172: * @param sRightTitle
173: * DOCUMENT ME!
174: * @param in
175: * DOCUMENT ME!
176: * @param parser
177: * DOCUMENT ME!
178: *
179: * @throws DiffException
180: * DOCUMENT ME!
181: * @throws IOException
182: * DOCUMENT ME!
183: */
184: public DiffViewer(String sLeftTitle, String sRightTitle, Reader in,
185: DiffParser parser, GruntspudContext context)
186: throws DiffException, IOException {
187: this (sLeftTitle, sRightTitle, in, parser, false, context);
188: }
189:
190: public void setAvailableActions() {
191:
192: }
193:
194: /**
195: * DOCUMENT ME!
196: *
197: * @param proportionalLocation
198: * DOCUMENT ME!
199: */
200: public void setDividerLocation(double proportionalLocation) {
201: m_SplitPane.setDividerLocation(proportionalLocation);
202: }
203:
204: /** Creates a DiffDisplay used to display the differences for a file. */
205: protected DiffDisplay createDiffDisplay(GruntspudContext context) {
206: return new DiffDisplay(context);
207: }
208:
209: /** Gets the tab size. */
210: public int getTabSize() {
211: return m_Left.getTabSize();
212: }
213:
214: /** Sets the tab size. */
215: public void setTabSize(int nTabSize) {
216: m_Left.setTabSize(nTabSize);
217: m_Right.setTabSize(nTabSize);
218: }
219:
220: /** Gets the font used to display the differences. */
221: public Font getDisplayFont() {
222: return m_Left.getDisplayFont();
223: }
224:
225: /** Sets the font used to display the differences. */
226: public void setDisplayFont(Font font) {
227: m_Left.setDisplayFont(font);
228: m_Right.setDisplayFont(font);
229: }
230:
231: /** Gets the style used to display differences of the supplied type. */
232: public TextStyle getStyle(DiffType type) {
233: return m_Left.getStyle(type);
234: }
235:
236: /** Sets the style used to display differences for the supplied type. */
237: public void setStyle(DiffType type, TextStyle style) {
238: m_Left.setStyle(type, style);
239: m_Right.setStyle(type, style);
240: }
241:
242: void setScrollPosition(int lineNo) {
243: Point p = new Point(0, m_Left.lineYPos(lineNo));
244: m_LeftScroll.getViewport().setViewPosition(p);
245:
246: // repaint positions the viewport correctly
247: // why is that anyway?
248: m_LeftScroll.getViewport().repaint();
249: }
250:
251: private JPanel createNextPrevPanel(JCheckBox synchronizeScrollBars,
252: JButton prev, JButton next) {
253: JPanel p = new JPanel(new GridBagLayout());
254: GridBagConstraints gbc;
255: gbc = new GridBagConstraints();
256: gbc.fill = GridBagConstraints.BOTH;
257: gbc.anchor = GridBagConstraints.WEST;
258: gbc.weightx = 1.0;
259: gbc.insets = new Insets(2, 2, 2, 2);
260: UIUtil.jGridBagAdd(p, synchronizeScrollBars, gbc, 1);
261: gbc.weightx = 0.0;
262: UIUtil.jGridBagAdd(p, prev, gbc, GridBagConstraints.RELATIVE);
263: UIUtil.jGridBagAdd(p, next, gbc, GridBagConstraints.REMAINDER);
264: return p;
265: }
266:
267: /**
268: * A DiffProcessor which adds appropriate lines to the DiffViewers to
269: * display the differences graphically.
270: */
271: private static class DiffDisplayInitializer extends DiffProcessor {
272: private DiffDisplay m_File1;
273: private DiffDisplay m_File2;
274: private boolean m_bInvert;
275:
276: public DiffDisplayInitializer(Reader in, DiffParser parser,
277: DiffDisplay file1, DiffDisplay file2, boolean bInvert) {
278: super (in, parser);
279: m_File1 = file1;
280: m_File2 = file2;
281: m_bInvert = bInvert;
282: }
283:
284: protected void printLineInFile1(int nLineNum, DiffType type,
285: String sLine) {
286: if (m_bInvert) {
287: if (type == DiffType.INSERTION)
288: type = DiffType.DELETION;
289: else if (type == DiffType.DELETION)
290: type = DiffType.INSERTION;
291: m_File2.addLine(nLineNum, type, sLine);
292: } else
293: m_File1.addLine(nLineNum, type, sLine);
294: }
295:
296: protected void printNonExistantLineInFile1() {
297: if (m_bInvert)
298: m_File2.addNonExistantLine();
299: else
300: m_File1.addNonExistantLine();
301: }
302:
303: protected void printLineInFile2(int nLineNum, DiffType type,
304: String sLine) {
305: if (m_bInvert) {
306: if (type == DiffType.INSERTION)
307: type = DiffType.DELETION;
308: else if (type == DiffType.DELETION)
309: type = DiffType.INSERTION;
310: m_File1.addLine(nLineNum, type, sLine);
311: } else
312: m_File2.addLine(nLineNum, type, sLine);
313: }
314:
315: protected void printNonExistantLineInFile2() {
316: if (m_bInvert)
317: m_File1.addNonExistantLine();
318: else
319: m_File2.addNonExistantLine();
320: }
321: }
322:
323: /**
324: * A DiffParser which reverses the differences from a supplied DiffParser.
325: * This is useful to generate the contents of a file when you know the
326: * differences and the result of applying the differences but
327: */
328: private static class ReverseDiffParser implements DiffParser {
329: private DiffParser m_Parser;
330:
331: public ReverseDiffParser(DiffParser parser) {
332: m_Parser = parser;
333: }
334:
335: public Difference getNextDifference() throws DiffException {
336: Difference d = m_Parser.getNextDifference();
337: if (d == null)
338: return null;
339: if (d instanceof Insertion) {
340: Insertion i = (Insertion) d;
341: return new Deletion(i.getStartLineInFile2(), i
342: .getEndLineInFile2(), i.getStartLineInFile1(),
343: i.getInsertedText());
344: } else if (d instanceof Deletion) {
345: Deletion del = (Deletion) d;
346: return new Insertion(del.getStartLineInFile2(), del
347: .getStartLineInFile1(),
348: del.getEndLineInFile1(), del.getDeletedText());
349: } else {
350: Change c = (Change) d;
351: return new Change(d.getStartLineInFile2(), c
352: .getEndLineInFile2(), c.getStartLineInFile1(),
353: c.getEndLineInFile1(), c.getToText(), c
354: .getFromText());
355: }
356: }
357: }
358:
359: class NextAction extends AbstractNextAction {
360: public void actionPerformed(ActionEvent evt) {
361: m_Navi.advanceDiff(true);
362: repaint();
363: setAvailableActions();
364: }
365: }
366:
367: class PreviousAction extends AbstractPreviousAction {
368: public void actionPerformed(ActionEvent evt) {
369: m_Navi.advanceDiff(false);
370: repaint();
371: setAvailableActions();
372: }
373: }
374: }
|