KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > diff > builtin > visualizer > editable > EditableDiffView


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.diff.builtin.visualizer.editable;
21
22 import java.awt.event.*;
23 import java.awt.*;
24 import java.beans.PropertyChangeListener JavaDoc;
25 import java.beans.PropertyChangeSupport JavaDoc;
26 import java.beans.PropertyChangeEvent JavaDoc;
27 import java.util.*;
28 import java.util.logging.Logger JavaDoc;
29 import java.util.logging.Level JavaDoc;
30 import java.io.*;
31 import java.lang.reflect.InvocationTargetException JavaDoc;
32 import java.lang.reflect.Method JavaDoc;
33 import javax.swing.*;
34 import javax.swing.plaf.basic.BasicSplitPaneUI JavaDoc;
35 import javax.swing.plaf.basic.BasicSplitPaneDivider JavaDoc;
36 import javax.swing.event.DocumentListener JavaDoc;
37 import javax.swing.event.DocumentEvent JavaDoc;
38 import javax.swing.event.AncestorListener JavaDoc;
39 import javax.swing.event.AncestorEvent JavaDoc;
40 import javax.swing.text.*;
41 import org.netbeans.api.editor.fold.FoldHierarchy;
42 import org.netbeans.api.editor.fold.FoldUtilities;
43 import org.netbeans.api.editor.fold.FoldHierarchyListener;
44 import org.netbeans.api.editor.fold.FoldHierarchyEvent;
45 import org.netbeans.modules.diff.NestableDiffView;
46 import org.netbeans.modules.diff.builtin.provider.BuiltInDiffProvider;
47 import org.netbeans.modules.diff.builtin.visualizer.GraphicalDiffVisualizer;
48 import org.netbeans.modules.diff.builtin.visualizer.SourceTranslatorAction;
49
50 import org.openide.util.Lookup;
51 import org.openide.util.RequestProcessor;
52 import org.openide.util.NbBundle;
53 import org.openide.ErrorManager;
54 import org.openide.awt.UndoRedo;
55 import org.openide.filesystems.FileObject;
56 import org.openide.text.CloneableEditorSupport;
57 import org.openide.cookies.SaveCookie;
58 import org.openide.cookies.EditorCookie;
59 import org.openide.loaders.DataObject;
60 import org.netbeans.api.diff.Difference;
61 import org.netbeans.api.diff.StreamSource;
62 import org.netbeans.api.diff.DiffView;
63 import org.netbeans.spi.diff.DiffProvider;
64 import org.netbeans.spi.diff.DiffVisualizer;
65
66 /**
67  * Panel that shows differences between two files. The code here was originally distributed among DiffPanel and
68  * DiffComponent classes.
69  *
70  * @author Maros Sandor
71  */

72 public class EditableDiffView implements DiffView, NestableDiffView, DocumentListener JavaDoc, AncestorListener JavaDoc, PropertyChangeListener JavaDoc {
73
74     // === Default Diff Colors ===========================================================
75
private Color colorMissing = new Color(255, 160, 180);
76     private Color colorAdded = new Color(180, 255, 180);
77     private Color colorChanged = new Color(160, 200, 255);
78     private Color colorLines = Color.DARK_GRAY;
79     private Color COLOR_READONLY_BG = new Color(240,240,240);
80
81     private final Difference [] NO_DIFFERENCES = new Difference[0];
82     
83     /**
84      * Left (first) half of the Diff view, contains the editor pane, actions bar and line numbers bar.
85      */

86     private DiffContentPanel jEditorPane1;
87
88     /**
89      * Right (second) half of the Diff view, contains the editor pane, actions bar and line numbers bar.
90      */

91     private DiffContentPanel jEditorPane2;
92
93     private boolean secondSourceAvailable;
94     private boolean firstSourceAvailable;
95     
96     private JViewport jViewport2;
97
98     final JLabel fileLabel1 = new JLabel();
99     final JLabel fileLabel2 = new JLabel();
100     final JPanel filePanel1 = new JPanel();
101     final JPanel filePanel2 = new JPanel();
102     final JSplitPane jSplitPane1 = new JSplitPane();
103
104     private int diffSerial;
105     private Difference[] diffs = NO_DIFFERENCES;
106    
107     private int currentDiffIndex = -1;
108     
109     private int totalHeight = 0;
110     private int totalLines = 0;
111
112     private int horizontalScroll1ChangedValue = -1;
113     private int horizontalScroll2ChangedValue = -1;
114     
115     private RequestProcessor.Task refreshDiffTask;
116     private DiffViewManager manager;
117     
118     private boolean actionsEnabled;
119     private DiffSplitPaneUI spui;
120     
121     /**
122      * The right pane is editable IFF editableCookie is not null.
123      */

124     private EditorCookie.Observable editableCookie;
125     private Document editableDocument;
126     private UndoRedo.Manager editorUndoRedo;
127
128     public EditableDiffView() {
129     }
130
131     public EditableDiffView(final StreamSource ss1, final StreamSource ss2) throws IOException {
132         refreshDiffTask = RequestProcessor.getDefault().create(new RefreshDiffTask());
133         initColors();
134         String JavaDoc title1 = ss1.getTitle();
135         if (title1 == null) title1 = NbBundle.getMessage(EditableDiffView.class, "CTL_DiffPanel_NoTitle"); // NOI18N
136
String JavaDoc title2 = ss2.getTitle();
137         if (title2 == null) title2 = NbBundle.getMessage(EditableDiffView.class, "CTL_DiffPanel_NoTitle"); // NOI18N
138
String JavaDoc mimeType1 = ss1.getMIMEType();
139         String JavaDoc mimeType2 = ss2.getMIMEType();
140         if (mimeType1 == null) mimeType1 = mimeType2;
141         if (mimeType2 == null) mimeType2 = mimeType1;
142         
143         actionsEnabled = ss2.isEditable();
144                 
145         jEditorPane1 = new DiffContentPanel(this, true);
146         jEditorPane2 = new DiffContentPanel(this, false);
147         
148         initComponents ();
149         jSplitPane1.setName(org.openide.util.NbBundle.getMessage(EditableDiffView.class, "DiffComponent.title")); // NOI18N
150
spui = new DiffSplitPaneUI(jSplitPane1);
151         jSplitPane1.setUI(spui);
152         jSplitPane1.setResizeWeight(0.5);
153         jSplitPane1.setDividerSize(32);
154         jSplitPane1.putClientProperty("PersistenceType", "Never"); // NOI18N
155
jSplitPane1.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(EditableDiffView.class, "ACS_DiffPanelA11yName")); // NOI18N
156
jSplitPane1.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(EditableDiffView.class, "ACS_DiffPanelA11yDesc")); // NOI18N
157
jEditorPane1.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(EditableDiffView.class, "ACS_EditorPane1A11yName")); // NOI18N
158
jEditorPane1.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(EditableDiffView.class, "ACS_EditorPane1A11yDescr")); // NOI18N
159
jEditorPane2.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(EditableDiffView.class, "ACS_EditorPane2A11yName")); // NOI18N
160
jEditorPane2.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(EditableDiffView.class, "ACS_EditorPane2A11yDescr")); // NOI18N
161

162         jSplitPane1.addAncestorListener(this);
163         
164         setSourceTitle(fileLabel1, title1);
165         setSourceTitle(fileLabel2, title2);
166         
167         final String JavaDoc f1 = mimeType1;
168         final String JavaDoc f2 = mimeType2;
169         try {
170             Runnable JavaDoc awtTask = new Runnable JavaDoc() {
171                 public void run() {
172                     jEditorPane1.getEditorPane().setEditorKit(CloneableEditorSupport.getEditorKit(f1));
173                     jEditorPane2.getEditorPane().setEditorKit(CloneableEditorSupport.getEditorKit(f2));
174                     
175                     try {
176                         setSource1(ss1);
177                         setSource2(ss2);
178                     } catch (IOException ioex) {
179                         org.openide.ErrorManager.getDefault().notify(ioex);
180                     }
181                     
182                     if (!secondSourceAvailable) {
183                         jEditorPane2.getEditorPane().setText(NbBundle.getMessage(EditableDiffView.class, "CTL_DiffPanel_NoContent")); // NOI18N
184
}
185                     if (!firstSourceAvailable) {
186                         jEditorPane1.getEditorPane().setText(NbBundle.getMessage(EditableDiffView.class, "CTL_DiffPanel_NoContent")); // NOI18N
187
}
188
189                     Color borderColor = UIManager.getColor("scrollpane_border"); // NOI18N
190
if (borderColor == null) borderColor = UIManager.getColor("controlShadow"); // NOI18N
191

192                     jEditorPane1.getScrollPane().setBorder(null);
193                     jEditorPane2.getScrollPane().setBorder(null);
194                     
195                     jEditorPane1.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, borderColor));
196                     jEditorPane2.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, borderColor));
197                     jSplitPane1.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, borderColor));
198                 }
199             };
200             if (SwingUtilities.isEventDispatchThread()) {
201                 awtTask.run();
202             } else {
203                  SwingUtilities.invokeAndWait(awtTask);
204             }
205         } catch (InterruptedException JavaDoc e) {
206             ErrorManager err = ErrorManager.getDefault();
207             err.notify(e);
208         } catch (InvocationTargetException JavaDoc e) {
209             ErrorManager err = ErrorManager.getDefault();
210             err.notify(e);
211         }
212
213         refreshDiffTask.run();
214         
215         manager = new DiffViewManager(this);
216         manager.init();
217     }
218
219     /**
220      * @return true if Move, Replace, Insert and Move All actions should be visible and enabled, false otherwise
221      */

222     public boolean isActionsEnabled() {
223         return actionsEnabled;
224     }
225    
226     private void initColors() {
227         Lookup.Result<DiffVisualizer> dv = Lookup.getDefault().lookup(new Lookup.Template<DiffVisualizer>(DiffVisualizer.class));
228         Collection c = dv.allInstances();
229         for (Iterator i = c.iterator(); i.hasNext();) {
230             Object JavaDoc o = i.next();
231             if (o instanceof GraphicalDiffVisualizer) {
232                 GraphicalDiffVisualizer gdv = (GraphicalDiffVisualizer) o;
233                 colorAdded = gdv.getColorAdded();
234                 colorChanged = gdv.getColorChanged();
235                 colorMissing = gdv.getColorMissing();
236             }
237         }
238     }
239
240     public void ancestorAdded(AncestorEvent JavaDoc event) {
241         expandFolds();
242         initGlobalSizes();
243         addChangeListeners();
244         refreshDiff(50);
245
246         if (editableCookie == null) return;
247         refreshEditableDocument();
248         editableCookie.addPropertyChangeListener(this);
249     }
250
251     private void refreshEditableDocument() {
252         Document doc = null;
253         try {
254             doc = editableCookie.openDocument();
255         } catch (IOException e) {
256             Logger.getLogger(EditableDiffView.class.getName()).log(Level.INFO, "Getting new Document from EditorCookie", e); // NOI18N
257
return;
258         }
259         editableDocument.removeDocumentListener(this);
260         if (doc != editableDocument) {
261             editableDocument = doc;
262             jEditorPane2.getEditorPane().setDocument(editableDocument);
263             refreshDiff(20);
264         }
265         editableDocument.addDocumentListener(this);
266     }
267
268     public void ancestorRemoved(AncestorEvent JavaDoc event) {
269         if (editableCookie != null) {
270             editableDocument.removeDocumentListener(this);
271             saveModifiedDocument();
272             editableCookie.removePropertyChangeListener(this);
273             if (editableCookie.getOpenedPanes() == null) {
274                 editableCookie.close();
275             }
276         }
277     }
278
279     private void saveModifiedDocument() {
280         DataObject dao = (DataObject) editableDocument.getProperty(Document.StreamDescriptionProperty);
281         if (dao != null) {
282             SaveCookie sc = dao.getCookie(SaveCookie.class);
283             if (sc != null) {
284                 try {
285                     sc.save();
286                 } catch (IOException e) {
287                     Logger.getLogger(EditableDiffView.class.getName()).log(Level.INFO, "Error saving Diff document", e); // NOI18N
288
}
289             }
290         }
291     }
292
293     public void ancestorMoved(AncestorEvent JavaDoc event) {
294     }
295
296     public void insertUpdate(DocumentEvent JavaDoc e) {
297         refreshDiff(50);
298     }
299
300     public void removeUpdate(DocumentEvent JavaDoc e) {
301         refreshDiff(50);
302     }
303
304     public void changedUpdate(DocumentEvent JavaDoc e) {
305         refreshDiff(50);
306     }
307     
308     Color getColor(Difference ad) {
309         if (ad.getType() == Difference.ADD) return colorAdded;
310         if (ad.getType() == Difference.CHANGE) return colorChanged;
311         return colorMissing;
312     }
313     
314     JComponent getMyDivider() {
315         return spui.divider.getDivider();
316     }
317
318     DiffContentPanel getEditorPane1() {
319         return jEditorPane1;
320     }
321
322     DiffContentPanel getEditorPane2() {
323         return jEditorPane2;
324     }
325
326     public DiffViewManager getManager() {
327         return manager;
328     }
329
330     Difference[] getDifferences() {
331         return diffs;
332     }
333
334     /**
335      * Rolls back a difference in the second document.
336      *
337      * @param diff a difference to roll back, null to remove all differences
338      */

339     void rollback(Difference diff) {
340         if (diff == null) {
341             try {
342                 Document dest = getEditorPane2().getEditorPane().getDocument();
343                 Document src = getEditorPane1().getEditorPane().getDocument();
344                 dest.remove(0, dest.getLength());
345                 dest.insertString(0, src.getText(0, src.getLength()), null);
346             } catch (BadLocationException e) {
347                 ErrorManager.getDefault().notify(e);
348             }
349             return;
350         }
351         try {
352             Document document = getEditorPane2().getEditorPane().getDocument();
353             if (diff.getType() == Difference.ADD) {
354                 int start = DiffViewManager.getRowStartFromLineOffset(document, diff.getSecondStart() - 1);
355                 int end = DiffViewManager.getRowStartFromLineOffset(document, diff.getSecondEnd());
356                 document.remove(start, end - start);
357             } else if (diff.getType() == Difference.DELETE) {
358                 int start = DiffViewManager.getRowStartFromLineOffset(document, diff.getSecondStart());
359                 document.insertString(start, diff.getFirstText(), null);
360             } else {
361                 int start = DiffViewManager.getRowStartFromLineOffset(document, diff.getSecondStart() - 1);
362                 int end = DiffViewManager.getRowStartFromLineOffset(document, diff.getSecondEnd());
363                 document.remove(start, end - start);
364                 document.insertString(start, diff.getFirstText(), null);
365             }
366         } catch (BadLocationException e) {
367             ErrorManager.getDefault().notify(e);
368         }
369     }
370
371     class DiffSplitPaneUI extends BasicSplitPaneUI JavaDoc {
372
373         final DiffSplitPaneDivider divider;
374
375         public DiffSplitPaneUI(JSplitPane splitPane) {
376             this.splitPane = splitPane;
377             divider = new DiffSplitPaneDivider(this, EditableDiffView.this);
378         }
379
380         public BasicSplitPaneDivider JavaDoc createDefaultDivider() {
381             return divider;
382         }
383     }
384     
385     public boolean requestFocusInWindow() {
386         return jEditorPane1.requestFocusInWindow();
387     }
388
389     public JComponent getComponent() {
390         return jSplitPane1;
391     }
392
393     public int getDifferenceCount() {
394         return diffs.length;
395     }
396
397     public boolean canSetCurrentDifference() {
398         return true;
399     }
400
401     public void setCurrentDifference(int diffNo) throws UnsupportedOperationException JavaDoc {
402         if (diffNo < -1 || diffNo >= diffs.length) throw new IllegalArgumentException JavaDoc("Illegal difference number: " + diffNo); // NOI18N
403
if (diffNo == -1) {
404         } else {
405             currentDiffIndex = diffNo;
406             showCurrentLine();
407         }
408     }
409
410     public int getCurrentDifference() throws UnsupportedOperationException JavaDoc {
411         int firstVisibleLine;
412         int lastVisibleLine;
413         int candidate = currentDiffIndex;
414         if (jViewport2 != null) {
415             int viewHeight = jViewport2.getViewSize().height;
416             java.awt.Point JavaDoc p1;
417             initGlobalSizes(); // The window might be resized in the mean time.
418
p1 = jViewport2.getViewPosition();
419             int HALFLINE_CEILING = 2; // compensation for rounding error and partially visible lines
420
float firstPct = ((float)p1.y / (float)viewHeight);
421             firstVisibleLine = (int) (firstPct * totalLines) + HALFLINE_CEILING;
422             float lastPct = ((float)(jViewport2.getHeight() + p1.y) / (float)viewHeight);
423             lastVisibleLine = (int) (lastPct * totalLines) - HALFLINE_CEILING;
424
425             for (int i = 0; i<diffs.length; i++) {
426                 int startLine = diffs[i].getSecondStart();
427                 int endLine = diffs[i].getSecondEnd(); // there no remove changes in right pane
428
if (firstVisibleLine < startLine && startLine < lastVisibleLine
429                 || firstVisibleLine < endLine && endLine < lastVisibleLine) {
430                     if (i == currentDiffIndex) {
431                         return currentDiffIndex; // current is visible, eliminate hazards use it.
432
}
433                     candidate = i; // takes last visible, optimalized for Next>
434
}
435             }
436         }
437
438         return candidate;
439     }
440
441     public JToolBar getToolBar() {
442         return null;
443     }
444
445     private void showCurrentLine() {
446         Difference diff = diffs[currentDiffIndex];
447         
448         int off1, off2;
449         initGlobalSizes(); // The window might be resized in the mean time.
450
try {
451             off1 = org.openide.text.NbDocument.findLineOffset((StyledDocument) jEditorPane1.getEditorPane().getDocument(), diff.getFirstStart() - 1);
452             off2 = org.openide.text.NbDocument.findLineOffset((StyledDocument) jEditorPane2.getEditorPane().getDocument(), diff.getSecondStart() - 1);
453
454             jEditorPane1.getEditorPane().setCaretPosition(off1);
455             jEditorPane2.getEditorPane().setCaretPosition(off2);
456         } catch (IndexOutOfBoundsException JavaDoc ex) {
457             ErrorManager.getDefault().notify(ex);
458         }
459
460         // scroll the left pane accordingly
461
manager.scroll();
462     }
463     
464     /** This method is called from within the constructor to initialize the form.
465      */

466     private void initComponents() {
467         java.awt.GridBagConstraints JavaDoc gridBagConstraints;
468
469         jSplitPane1.setDividerSize(4);
470         filePanel1.setLayout(new java.awt.GridBagLayout JavaDoc());
471
472         gridBagConstraints = new java.awt.GridBagConstraints JavaDoc();
473         gridBagConstraints.gridx = 0;
474         gridBagConstraints.gridy = 1;
475         gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
476         gridBagConstraints.weightx = 1.0;
477         gridBagConstraints.weighty = 1.0;
478         filePanel1.add(jEditorPane1, gridBagConstraints);
479
480         fileLabel1.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
481         fileLabel1.setLabelFor(jEditorPane1);
482         gridBagConstraints = new java.awt.GridBagConstraints JavaDoc();
483         gridBagConstraints.gridx = 0;
484         gridBagConstraints.gridy = 0;
485         gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
486         gridBagConstraints.weightx = 1.0;
487         gridBagConstraints.insets = new java.awt.Insets JavaDoc(4, 4, 4, 4);
488         filePanel1.add(fileLabel1, gridBagConstraints);
489
490         jSplitPane1.setLeftComponent(filePanel1);
491
492         filePanel2.setLayout(new java.awt.GridBagLayout JavaDoc());
493
494         gridBagConstraints = new java.awt.GridBagConstraints JavaDoc();
495         gridBagConstraints.gridx = 0;
496         gridBagConstraints.gridy = 1;
497         gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
498         gridBagConstraints.weightx = 1.0;
499         gridBagConstraints.weighty = 1.0;
500         filePanel2.add(jEditorPane2, gridBagConstraints);
501
502         fileLabel2.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
503         fileLabel2.setLabelFor(jEditorPane2);
504         gridBagConstraints = new java.awt.GridBagConstraints JavaDoc();
505         gridBagConstraints.gridx = 0;
506         gridBagConstraints.gridy = 0;
507         gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
508         gridBagConstraints.weightx = 1.0;
509         gridBagConstraints.insets = new java.awt.Insets JavaDoc(4, 4, 4, 4);
510         filePanel2.add(fileLabel2, gridBagConstraints);
511
512         jSplitPane1.setRightComponent(filePanel2);
513     }
514
515     // Code for dispatching events from components to event handlers.
516
private void expandFolds(JEditorPane pane) {
517         final FoldHierarchy fh = FoldHierarchy.get(pane);
518         FoldUtilities.expandAll(fh);
519         fh.addFoldHierarchyListener(new FoldHierarchyListener() {
520             public void foldHierarchyChanged(FoldHierarchyEvent evt) {
521                 FoldUtilities.expandAll(fh);
522             }
523         });
524     }
525
526     private void expandFolds() {
527         expandFolds(jEditorPane1.getEditorPane());
528         expandFolds(jEditorPane2.getEditorPane());
529     }
530
531     private void initGlobalSizes() {
532         StyledDocument doc1 = (StyledDocument) jEditorPane1.getEditorPane().getDocument();
533         StyledDocument doc2 = (StyledDocument) jEditorPane2.getEditorPane().getDocument();
534         int numLines1 = org.openide.text.NbDocument.findLineNumber(doc1, doc1.getEndPosition().getOffset());
535         int numLines2 = org.openide.text.NbDocument.findLineNumber(doc2, doc2.getEndPosition().getOffset());
536
537         int numLines = Math.max(numLines1, numLines2);
538         if (numLines < 1) numLines = 1;
539         this.totalLines = numLines;
540         int totHeight = jEditorPane1.getSize().height;
541         int value = jEditorPane2.getSize().height;
542         if (value > totHeight) totHeight = value;
543         this.totalHeight = totHeight;
544     }
545
546     // NestableDiffView implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
547

548     public void joinScrollPane(final JScrollPane pane) {
549         jEditorPane1.getScrollPane().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
550         jEditorPane1.getScrollPane().setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
551         jEditorPane2.getScrollPane().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
552         jEditorPane2.getScrollPane().setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
553         jEditorPane1.getScrollPane().setWheelScrollingEnabled(false);
554         jEditorPane2.getScrollPane().setWheelScrollingEnabled(false);
555         jEditorPane1.getScrollPane().addMouseWheelListener(new MouseWheelListener() {
556             public void mouseWheelMoved(MouseWheelEvent e) {
557                 pane.dispatchEvent(e);
558             }
559         });
560         jEditorPane2.getScrollPane().addMouseWheelListener(new MouseWheelListener() {
561             public void mouseWheelMoved(MouseWheelEvent e) {
562                 pane.dispatchEvent(e);
563             }
564         });
565         jEditorPane1.getEditorPane().getCaret().setVisible(false);
566         jEditorPane2.getEditorPane().getCaret().setVisible(false);
567
568         // map JEditorPane keystroke to JScrollPane action registered for even keystroke
569
KeyStroke[] keyStrokes = new KeyStroke[] {
570             KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0),
571             KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0),
572
573             KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0),
574             KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0),
575
576             KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0),
577             KeyStroke.getKeyStroke(KeyEvent.VK_HOME, InputEvent.CTRL_MASK),
578
579             KeyStroke.getKeyStroke(KeyEvent.VK_HOME, InputEvent.CTRL_MASK),
580             KeyStroke.getKeyStroke(KeyEvent.VK_HOME, InputEvent.CTRL_MASK),
581             
582             KeyStroke.getKeyStroke(KeyEvent.VK_END, 0),
583             KeyStroke.getKeyStroke(KeyEvent.VK_END, InputEvent.CTRL_MASK),
584
585             KeyStroke.getKeyStroke(KeyEvent.VK_END, InputEvent.CTRL_MASK),
586             KeyStroke.getKeyStroke(KeyEvent.VK_END, InputEvent.CTRL_MASK),
587
588             KeyStroke.getKeyStroke(KeyEvent.VK_END, 0),
589             KeyStroke.getKeyStroke(KeyEvent.VK_END, 0),
590
591             KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0),
592             KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0),
593
594             KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0),
595             KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0),
596
597             KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0),
598             KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0),
599
600             KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0),
601             KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0),
602         };
603
604         for (int i = 0; i<keyStrokes.length; i+=2) {
605             KeyStroke stroke = keyStrokes[i];
606             KeyStroke stroke2 = keyStrokes[i+1];
607             Object JavaDoc pane1Key = jEditorPane1.getInputMap().get(stroke);
608             Object JavaDoc pane2Key = jEditorPane2.getInputMap().get(stroke);
609             Object JavaDoc scrollKey = pane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).get(stroke2);
610             if (scrollKey != null) {
611                 final Action scrollAction = pane.getActionMap().get(scrollKey);
612                 jEditorPane1.getActionMap().put(pane1Key, new SourceTranslatorAction(scrollAction, pane));
613                 jEditorPane2.getActionMap().put(pane2Key, new SourceTranslatorAction(scrollAction, pane));
614             } else {
615                 // System.err.println("No JScrollPane binding for " + stroke); // HOME, END
616
}
617         }
618
619     }
620
621     public int getInnerScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
622         return jEditorPane1.getEditorPane().getScrollableUnitIncrement(visibleRect, orientation, direction);
623     }
624
625     public int getInnerScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
626         return jEditorPane1.getEditorPane().getScrollableBlockIncrement(visibleRect, orientation, direction);
627     }
628     
629     public int getInnerWidth() {
630         Dimension d1 = jEditorPane1.getScrollPane().getViewport().getView().getPreferredSize();
631         Dimension d2 = jEditorPane2.getScrollPane().getViewport().getView().getPreferredSize();
632         return Math.max(d1.width, d2.width) * 2;
633     }
634
635     public void setInnerWidth(int width) {
636         Dimension dim = jEditorPane1.getScrollPane().getViewport().getViewSize();
637         dim.width = width/2;
638         jEditorPane1.getScrollPane().getViewport().setViewSize(dim);
639
640         dim = jEditorPane2.getScrollPane().getViewport().getViewSize();
641         dim.width = width/2;
642         jEditorPane2.getScrollPane().getViewport().setViewSize(dim);
643
644         jSplitPane1.setDividerLocation(0.5);
645     }
646
647     public void setHorizontalPosition(int pos) {
648         pos /= 2;
649
650         Point p = jEditorPane1.getScrollPane().getViewport().getViewPosition();
651         p.x = pos;
652         jEditorPane1.getScrollPane().getViewport().setViewPosition(p);
653
654         p = jEditorPane2.getScrollPane().getViewport().getViewPosition();
655         p.x = pos;
656         jEditorPane2.getScrollPane().getViewport().setViewPosition(p);
657     }
658
659     /** Return change's top y-axis position. */
660     public int getChangeY(int change) {
661         Difference diff = diffs[change];
662         int line = diff.getFirstStart();
663         int padding = 5;
664         if (line <= 5) {
665             padding = line/2;
666         }
667         initGlobalSizes();
668         int ypos = (totalHeight*(line - padding - 1))/(totalLines + 1);
669         ypos += fileLabel1.getHeight();
670         return ypos;
671     }
672
673     private void joinScrollBars() {
674         final JScrollBar scrollBarH1 = jEditorPane1.getScrollPane().getHorizontalScrollBar();
675         final JScrollBar scrollBarH2 = jEditorPane2.getScrollPane().getHorizontalScrollBar();
676
677         scrollBarH1.getModel().addChangeListener(new javax.swing.event.ChangeListener JavaDoc() {
678             public void stateChanged(javax.swing.event.ChangeEvent JavaDoc e) {
679                 int value = scrollBarH1.getValue();
680                 if (value == horizontalScroll1ChangedValue) return;
681                 int max1 = scrollBarH1.getMaximum();
682                 int max2 = scrollBarH2.getMaximum();
683                 int ext1 = scrollBarH1.getModel().getExtent();
684                 int ext2 = scrollBarH2.getModel().getExtent();
685                 if (max1 == ext1) horizontalScroll2ChangedValue = 0;
686                 else horizontalScroll2ChangedValue = (value*(max2 - ext2))/(max1 - ext1);
687                 horizontalScroll1ChangedValue = -1;
688                 scrollBarH2.setValue(horizontalScroll2ChangedValue);
689             }
690         });
691         scrollBarH2.getModel().addChangeListener(new javax.swing.event.ChangeListener JavaDoc() {
692             public void stateChanged(javax.swing.event.ChangeEvent JavaDoc e) {
693                 int value = scrollBarH2.getValue();
694                 if (value == horizontalScroll2ChangedValue) return;
695                 int max1 = scrollBarH1.getMaximum();
696                 int max2 = scrollBarH2.getMaximum();
697                 int ext1 = scrollBarH1.getModel().getExtent();
698                 int ext2 = scrollBarH2.getModel().getExtent();
699                 if (max2 == ext2) horizontalScroll1ChangedValue = 0;
700                 else horizontalScroll1ChangedValue = (value*(max1 - ext1))/(max2 - ext2);
701                 horizontalScroll2ChangedValue = -1;
702                 scrollBarH1.setValue(horizontalScroll1ChangedValue);
703             }
704         });
705         jSplitPane1.setDividerLocation(0.5);
706     }
707
708     private void customizeEditor(JEditorPane editor) {
709         StyledDocument doc;
710         Document document = editor.getDocument();
711         try {
712             doc = (StyledDocument) editor.getDocument();
713         } catch(ClassCastException JavaDoc e) {
714             doc = new DefaultStyledDocument();
715             try {
716                 doc.insertString(0, document.getText(0, document.getLength()), null);
717             } catch (BadLocationException ble) {
718                 // leaving the document empty
719
}
720             editor.setDocument(doc);
721         }
722     }
723     
724     private void addChangeListeners() {
725         jEditorPane1.getEditorPane().addPropertyChangeListener("font", new java.beans.PropertyChangeListener JavaDoc() { // NOI18N
726
public void propertyChange(java.beans.PropertyChangeEvent JavaDoc evt) {
727                 javax.swing.SwingUtilities.invokeLater(new Runnable JavaDoc() {
728                     public void run() {
729                         diffSerial++; // we need to re-compute decorations, font size changed
730
initGlobalSizes();
731                         jEditorPane1.onUISettingsChanged();
732                         getComponent().revalidate();
733                         getComponent().repaint();
734                     }
735                 });
736             }
737         });
738         jEditorPane2.getEditorPane().addPropertyChangeListener("font", new java.beans.PropertyChangeListener JavaDoc() { // NOI18N
739
public void propertyChange(java.beans.PropertyChangeEvent JavaDoc evt) {
740                 javax.swing.SwingUtilities.invokeLater(new Runnable JavaDoc() {
741                     public void run() {
742                         diffSerial++; // we need to re-compute decorations, font size changed
743
initGlobalSizes();
744                         jEditorPane2.onUISettingsChanged();
745                         getComponent().revalidate();
746                         getComponent().repaint();
747                     }
748                 });
749             }
750         });
751     }
752
753     private void setSource1(StreamSource ss) throws IOException {
754         firstSourceAvailable = false;
755         EditorKit kit = jEditorPane1.getEditorPane().getEditorKit();
756         if (kit == null) throw new IOException("Missing Editor Kit"); // NOI18N
757

758         Document sdoc = getSourceDocument(ss);
759         Document doc = sdoc != null ? sdoc : kit.createDefaultDocument();
760         if (!(doc instanceof StyledDocument)) {
761             doc = new DefaultStyledDocument(new StyleContext());
762             kit = new StyledEditorKit();
763             jEditorPane1.getEditorPane().setEditorKit(kit);
764         }
765         if (sdoc == null) {
766             Reader r = ss.createReader();
767             if (r != null) {
768                 firstSourceAvailable = true;
769                 try {
770                     kit.read(r, doc, 0);
771                 } catch (javax.swing.text.BadLocationException JavaDoc e) {
772                     throw new IOException("Can not locate the beginning of the document."); // NOI18N
773
} finally {
774                     r.close();
775                 }
776             }
777         } else {
778             firstSourceAvailable = true;
779         }
780         jEditorPane1.initActions();
781         jEditorPane1.getEditorPane().setDocument(doc);
782         customizeEditor(jEditorPane1.getEditorPane());
783         jViewport2 = jEditorPane2.getScrollPane().getViewport();
784     }
785     
786     private Document getSourceDocument(StreamSource ss) {
787         Document sdoc = null;
788         FileObject fo = ss.getLookup().lookup(FileObject.class);
789         if (fo != null) {
790             try {
791                 DataObject dao = DataObject.find(fo);
792                 EditorCookie ec = dao.getCookie(EditorCookie.class);
793                 if (ec != null) {
794                     sdoc = ec.openDocument();
795                 }
796             } catch (Exception JavaDoc e) {
797                 // fallback to other means of obtaining the source
798
}
799         } else {
800             sdoc = ss.getLookup().lookup(Document.class);
801         }
802         return sdoc;
803     }
804
805     private void setSource2(StreamSource ss) throws IOException {
806         secondSourceAvailable = false;
807         EditorKit kit = jEditorPane2.getEditorPane().getEditorKit();
808         if (kit == null) throw new IOException("Missing Editor Kit"); // NOI18N
809

810         Document sdoc = getSourceDocument(ss);
811         if (sdoc != null && ss.isEditable()) {
812             DataObject dao = (DataObject) sdoc.getProperty(Document.StreamDescriptionProperty);
813             if (dao != null) {
814                 EditorCookie cookie = dao.getCookie(EditorCookie.class);
815                 if (cookie instanceof EditorCookie.Observable) {
816                     editableCookie = (EditorCookie.Observable) cookie;
817                     editableDocument = sdoc;
818                     editorUndoRedo = getUndoRedo(cookie);
819                 }
820             }
821         }
822         Document doc = sdoc != null ? sdoc : kit.createDefaultDocument();
823         if (!(doc instanceof StyledDocument)) {
824             doc = new DefaultStyledDocument(new StyleContext());
825             kit = new StyledEditorKit();
826             jEditorPane2.getEditorPane().setEditorKit(kit);
827         }
828         if (sdoc == null) {
829             Reader r = ss.createReader();
830             if (r != null) {
831                 secondSourceAvailable = true;
832                 try {
833                     kit.read(r, doc, 0);
834                 } catch (javax.swing.text.BadLocationException JavaDoc e) {
835                     throw new IOException("Can not locate the beginning of the document."); // NOI18N
836
} finally {
837                     r.close();
838                 }
839             }
840         } else {
841             secondSourceAvailable = true;
842         }
843         jEditorPane2.initActions();
844         jSplitPane1.putClientProperty(UndoRedo.class, editorUndoRedo);
845         jEditorPane2.getEditorPane().setDocument(doc);
846         jEditorPane2.getEditorPane().setEditable(editableCookie != null);
847         if (jEditorPane2.getEditorPane().isEditable()) {
848             jEditorPane1.getEditorPane().setBackground(COLOR_READONLY_BG);
849         }
850         
851         customizeEditor(jEditorPane2.getEditorPane());
852         joinScrollBars();
853     }
854     
855     private UndoRedo.Manager getUndoRedo(EditorCookie cookie) {
856         // TODO: working around #96543
857
try {
858             Method JavaDoc method = CloneableEditorSupport.class.getDeclaredMethod("getUndoRedo"); // NOI18N
859
method.setAccessible(true);
860             return (UndoRedo.Manager) method.invoke(cookie);
861         } catch (Exception JavaDoc e) {
862             e.printStackTrace();
863         }
864         return null;
865     }
866
867     public void propertyChange(final PropertyChangeEvent JavaDoc evt) {
868         if (EditorCookie.Observable.PROP_DOCUMENT.equals(evt.getPropertyName())) {
869             SwingUtilities.invokeLater(new Runnable JavaDoc() {
870                 public void run() {
871                     refreshEditableDocument();
872                 }
873             });
874         }
875     }
876
877     public void setSourceTitle(JLabel label, String JavaDoc title) {
878         label.setText(title);
879         // Set the minimum size in 'x' direction to a low value, so that the splitter can be moved to corner locations
880
label.setMinimumSize(new Dimension(3, label.getMinimumSize().height));
881     }
882     
883     public void setDocument1(Document doc) {
884         if (doc != null) {
885             jEditorPane1.getEditorPane().setDocument(doc);
886         }
887     }
888     
889     public void setDocument2(Document doc) {
890         if (doc != null) {
891             jEditorPane2.getEditorPane().setDocument(doc);
892         }
893     }
894
895     private void refreshDiff(int delayMillis) {
896         refreshDiffTask.schedule(delayMillis);
897     }
898
899     public class RefreshDiffTask implements Runnable JavaDoc {
900
901         public void run() {
902             synchronized(EditableDiffView.this) {
903                 computeDiff();
904                 SwingUtilities.invokeLater(new Runnable JavaDoc() {
905                     public void run() {
906                         jEditorPane1.setCurrentDiff(diffs);
907                         jEditorPane2.setCurrentDiff(diffs);
908                         jSplitPane1.repaint();
909                     }
910                 });
911             }
912         }
913
914         private boolean equals(Difference[] a, Difference[] b) {
915             if (a == null || b == null || a.length != b.length) return false;
916             for (int i = 0; i < a.length; i++) {
917                 Difference ad = a[i];
918                 Difference bd = b[i];
919                 if (ad.getType() != bd.getType() ||
920                         ad.getFirstStart() != bd.getFirstStart() ||
921                         ad.getSecondStart() != bd.getSecondStart() ||
922                         ad.getFirstEnd() != bd.getFirstEnd() ||
923                         ad.getSecondEnd() != bd.getSecondEnd()) return false;
924             }
925             return true;
926         }
927
928         private void computeDiff() {
929             if (!secondSourceAvailable || !firstSourceAvailable) {
930                 diffs = NO_DIFFERENCES;
931                 return;
932             }
933             
934             Reader first = null;
935             Reader second = null;
936             try {
937                 first = new StringReader(jEditorPane1.getEditorPane().getDocument().getText(0, jEditorPane1.getEditorPane().getDocument().getLength()));
938                 second = new StringReader(jEditorPane2.getEditorPane().getDocument().getText(0, jEditorPane2.getEditorPane().getDocument().getLength()));
939             } catch (BadLocationException e) {
940                 ErrorManager.getDefault().notify(e);
941             }
942
943             DiffProvider diff = Lookup.getDefault().lookup(DiffProvider.class);
944             if (diff == null) {
945                 diffs = NO_DIFFERENCES;
946                 return;
947             }
948             boolean isTrim = false;
949             if (diff instanceof BuiltInDiffProvider) {
950                 isTrim = ((BuiltInDiffProvider) diff).isTrimLines();
951                 ((BuiltInDiffProvider) diff).setTrimLines(false);
952             }
953             try {
954                 diffs = diff.computeDiff(first, second);
955                 diffSerial++;
956             } catch (IOException e) {
957                 diffs = NO_DIFFERENCES;
958             }
959             if (diff instanceof BuiltInDiffProvider) {
960                 ((BuiltInDiffProvider) diff).setTrimLines(isTrim);
961             }
962         }
963     }
964     
965     int getDiffSerial() {
966         return diffSerial;
967     }
968
969     static Difference getFirstDifference(Difference [] diff, int line) {
970         if (line < 0) return null;
971         for (int i = 0; i < diff.length; i++) {
972             Difference difference = diff[i];
973             if (line < difference.getFirstStart()) return null;
974             if (difference.getType() == Difference.ADD && line == difference.getFirstStart()) return difference;
975             if (line <= difference.getFirstEnd()) return difference;
976         }
977         return null;
978     }
979
980     static Difference getSecondDifference(Difference [] diff, int line) {
981         if (line < 0) return null;
982         for (int i = 0; i < diff.length; i++) {
983             Difference difference = diff[i];
984             if (line < difference.getSecondStart()) return null;
985             if (difference.getType() == Difference.DELETE && line == difference.getSecondStart()) return difference;
986             if (line <= difference.getSecondEnd()) return difference;
987         }
988         return null;
989     }
990     
991     Color getColorLines() {
992         return colorLines;
993     }
994
995     private PropertyChangeSupport JavaDoc support = new PropertyChangeSupport JavaDoc(this);
996
997     public void addPropertyChangeListener(PropertyChangeListener JavaDoc l) {
998         support.addPropertyChangeListener(l);
999     }
1000
1001    public void removePropertyChangeListener(PropertyChangeListener JavaDoc l) {
1002        support.removePropertyChangeListener(l);
1003    }
1004}
1005
Popular Tags