1 19 20 package org.netbeans.modules.diff.builtin.visualizer; 21 22 import java.awt.Point ; 23 import java.awt.Rectangle ; 24 import java.awt.event.*; 25 import java.awt.event.ActionEvent ; 26 import java.awt.event.FocusEvent ; 27 import java.awt.event.FocusListener ; 28 import java.awt.Dimension ; 29 import java.awt.Component ; 30 import java.awt.Color ; 31 import java.beans.PropertyChangeEvent ; 32 import java.beans.PropertyChangeListener ; 33 import java.util.*; 34 import java.io.*; 35 import java.lang.reflect.InvocationTargetException ; 36 import javax.swing.*; 37 import javax.swing.text.*; 38 import org.netbeans.api.editor.fold.FoldHierarchy; 39 import org.netbeans.api.editor.fold.FoldUtilities; 40 import org.netbeans.modules.diff.NestableDiffView; 41 import org.netbeans.modules.diff.builtin.provider.BuiltInDiffProvider; 42 43 import org.openide.actions.CopyAction; 44 import org.openide.util.actions.ActionPerformer; 45 import org.openide.util.actions.CallbackSystemAction; 46 import org.openide.util.actions.SystemAction; 47 import org.openide.util.Lookup; 48 import org.openide.ErrorManager; 49 import org.netbeans.api.diff.Difference; 50 import org.netbeans.api.diff.StreamSource; 51 import org.netbeans.spi.diff.DiffProvider; 52 import org.netbeans.spi.diff.DiffVisualizer; 53 import org.netbeans.editor.EditorUI; 54 import org.netbeans.editor.ext.ExtCaret; 55 import org.openide.text.CloneableEditorSupport; 56 57 63 public class DiffViewImpl extends javax.swing.JPanel implements org.netbeans.api.diff.DiffView, javax.swing.event.CaretListener , NestableDiffView { 64 65 67 public static final java.awt.Color COLOR_MISSING = new java.awt.Color (255, 160, 180); 68 public static final java.awt.Color COLOR_ADDED = new java.awt.Color (180, 255, 180); 69 public static final java.awt.Color COLOR_CHANGED = new java.awt.Color (160, 200, 255); 70 71 private Difference[] diffs = null; 72 73 74 private int[][] diffShifts; 75 76 private java.awt.Color colorMissing = COLOR_MISSING; 77 private java.awt.Color colorAdded = COLOR_ADDED; 78 private java.awt.Color colorChanged = COLOR_CHANGED; 79 80 private int currentDiffLine = -1; 81 82 private int totalHeight = 0; 83 private int totalLines = 0; 84 85 private int horizontalScroll1ChangedValue = -1; 86 private int horizontalScroll2ChangedValue = -1; 87 88 private LinesComponent linesComp1; 89 private LinesComponent linesComp2; 90 91 private String source1; 92 private String source2; 93 94 private static final String PLAIN_TEXT_MIME = "text/plain"; 95 96 private int onLayoutLine; 97 private int onLayoutLength; 98 99 public DiffViewImpl() { 100 } 101 102 private static void copyStreamsCloseAll(Writer out, Reader in) throws IOException { 103 char[] buff = new char[4096]; 104 int n; 105 while ((n = in.read(buff)) > 0) { 106 out.write(buff, 0, n); 107 } 108 in.close(); 109 out.close(); 110 } 111 112 public DiffViewImpl(StreamSource ss1, StreamSource ss2) throws IOException { 113 Lookup.Result<DiffVisualizer> dv = Lookup.getDefault().lookup(new Lookup.Template<DiffVisualizer>(DiffVisualizer.class)); 114 for (DiffVisualizer diff: dv.allInstances()) { 115 if (diff instanceof GraphicalDiffVisualizer) { 116 GraphicalDiffVisualizer gdv = (GraphicalDiffVisualizer) diff; 117 colorAdded = gdv.getColorAdded(); 118 colorChanged = gdv.getColorChanged(); 119 colorMissing = gdv.getColorMissing(); 120 } 121 } 122 Reader r1 = ss1.createReader(); 123 Reader r2 = ss2.createReader(); 124 String title1 = ss1.getTitle(); 125 String title2 = ss2.getTitle(); 126 String mimeType1 = ss1.getMIMEType(); 127 String mimeType2 = ss2.getMIMEType(); 128 if (mimeType1 == null) mimeType1 = mimeType2; 129 if (mimeType2 == null) mimeType2 = mimeType1; 130 131 saveSources(r1, r2); 132 initComponents (); 133 setName(org.openide.util.NbBundle.getMessage(DiffViewImpl.class, "DiffComponent.title")); 134 initActions(); 135 jSplitPane1.setResizeWeight(0.5); 136 putClientProperty("PersistenceType", "Never"); 137 getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(DiffViewImpl.class, "ACS_DiffPanelA11yName")); getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(DiffViewImpl.class, "ACS_DiffPanelA11yDesc")); jEditorPane1.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(DiffViewImpl.class, "ACS_EditorPane1A11yName")); jEditorPane1.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(DiffViewImpl.class, "ACS_EditorPane1A11yDescr")); jEditorPane2.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(DiffViewImpl.class, "ACS_EditorPane2A11yName")); jEditorPane2.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(DiffViewImpl.class, "ACS_EditorPane2A11yDescr")); 144 if (source1 == null) jEditorPane1.setVisible(false); 145 if (source2 == null) jEditorPane2.setVisible(false); 146 147 if (r1 != null && r2 != null) { 148 DiffProvider provider = (DiffProvider) Lookup.getDefault().lookup(DiffProvider.class); 149 if (provider == null) { 150 provider = new BuiltInDiffProvider(); 151 } 152 diffs = provider.computeDiff(new StringReader(source1), new StringReader(source2)); 153 } else { 154 diffs = new Difference[0]; 155 } 156 diffShifts = new int[diffs.length][2]; 157 158 setSource1Title(title1); 159 setSource2Title(title2); 160 161 168 final String f1 = mimeType1; 169 final String f2 = mimeType2; 170 try { 171 Runnable awtTask = new Runnable () { 172 public void run() { 173 setMimeType1(f1); 174 setMimeType2(f2); 175 try { 176 if (source1 != null) setSource1(new StringReader(source1)); 177 if (source2 != null) setSource2(new StringReader(source2)); 178 } catch (IOException ioex) { 179 org.openide.ErrorManager.getDefault().notify(ioex); 180 } 181 insertEmptyLines(true); 182 setDiffHighlight(true); 183 insertEmptyLinesNotReported(); 184 185 Color borderColor = UIManager.getColor("scrollpane_border"); 186 if (borderColor == null) borderColor = UIManager.getColor("controlShadow"); 187 jScrollPane1.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, borderColor)); 188 jScrollPane2.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, borderColor)); 189 jSplitPane1.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, borderColor)); 190 } 191 }; 192 if (SwingUtilities.isEventDispatchThread()) { 193 awtTask.run(); 194 } else { 195 SwingUtilities.invokeAndWait(awtTask); 196 } 197 } catch (InterruptedException e) { 198 ErrorManager err = ErrorManager.getDefault(); 199 err.notify(e); 200 } catch (InvocationTargetException e) { 201 ErrorManager err = ErrorManager.getDefault(); 202 err.notify(e); 203 } 204 205 } 206 207 private void saveSources(Reader r1, Reader r2) throws IOException { 208 if (r1 != null) { 209 StringWriter sw = new StringWriter(); 210 copyStreamsCloseAll(sw, r1); 211 source1 = sw.toString(); 212 } 213 if (r2 != null) { 214 StringWriter sw = new StringWriter(); 215 copyStreamsCloseAll(sw, r2); 216 source2 = sw.toString(); 217 } 218 } 219 220 public boolean requestFocusInWindow() { 221 return jEditorPane1.requestFocusInWindow(); 222 } 223 224 public Component getComponent() { 225 return this; 226 } 227 228 public int getDifferenceCount() { 229 return diffs.length; 230 } 231 232 public boolean canSetCurrentDifference() { 233 return true; 234 } 235 236 public void setCurrentDifference(int diffNo) throws UnsupportedOperationException { 237 238 239 if (diffNo < -1 || diffNo >= diffs.length) throw new IllegalArgumentException ("Illegal difference number: " + diffNo); 240 241 if (diffNo == -1) { 242 if (linesComp1 != null) { 243 this.linesComp1.setActiveLine(-1); 244 linesComp1.repaint(); 245 } 246 247 if (linesComp2 != null) { 248 this.linesComp2.setActiveLine(-1); 249 linesComp2.repaint(); 250 } 251 } else { 252 currentDiffLine = diffNo; 253 showCurrentLine(); 254 } 255 } 256 257 public int getCurrentDifference() throws UnsupportedOperationException { 258 int firstVisibleLine; 259 int lastVisibleLine; 260 int candidate = currentDiffLine; 261 if (jViewport1 != null) { 262 int viewHeight = jViewport1.getViewSize().height; 263 java.awt.Point p1; 264 initGlobalSizes(); p1 = jViewport1.getViewPosition(); 266 int HALFLINE_CEILING = 2; float firstPct = ((float)p1.y / (float)viewHeight); 268 firstVisibleLine = (int) (firstPct * totalLines) + HALFLINE_CEILING; 269 float lastPct = ((float)(jViewport1.getHeight() + p1.y) / (float)viewHeight); 270 lastVisibleLine = (int) (lastPct * totalLines) - HALFLINE_CEILING; 271 272 for (int i = 0; i<diffs.length; i++) { 273 int startLine = diffShifts[i][0] + diffs[i].getFirstStart(); 274 int endLine = diffShifts[i][0] + diffs[i].getFirstEnd(); if (firstVisibleLine < startLine && startLine < lastVisibleLine 276 || firstVisibleLine < endLine && endLine < lastVisibleLine) { 277 if (i == currentDiffLine) { 278 return currentDiffLine; } 280 candidate = i; } 282 } 283 } 284 285 return candidate; 286 } 287 288 public JToolBar getToolBar() { 289 return null; 290 } 291 292 private void showCurrentLine() { 293 Difference diff = diffs[currentDiffLine]; 294 int line = diff.getFirstStart() + diffShifts[currentDiffLine][0]; 295 if (diff.getType() == Difference.ADD) line++; 296 int lf1 = diff.getFirstEnd() - diff.getFirstStart() + 1; 297 int lf2 = diff.getSecondEnd() - diff.getSecondStart() + 1; 298 int length = Math.max(lf1, lf2); 299 setCurrentLine(line, length); 300 } 301 302 307 private void initComponents() { 309 java.awt.GridBagConstraints gridBagConstraints; 310 311 setLayout(new java.awt.BorderLayout ()); 312 313 jSplitPane1.setDividerSize(4); 314 filePanel1.setLayout(new java.awt.GridBagLayout ()); 315 316 jEditorPane1.addCaretListener(this); 317 318 jScrollPane1.setViewportView(jEditorPane1); 319 320 gridBagConstraints = new java.awt.GridBagConstraints (); 321 gridBagConstraints.gridx = 0; 322 gridBagConstraints.gridy = 1; 323 gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; 324 gridBagConstraints.weightx = 1.0; 325 gridBagConstraints.weighty = 1.0; 326 filePanel1.add(jScrollPane1, gridBagConstraints); 327 328 fileLabel1.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); 329 fileLabel1.setLabelFor(jEditorPane1); 330 gridBagConstraints = new java.awt.GridBagConstraints (); 331 gridBagConstraints.gridx = 0; 332 gridBagConstraints.gridy = 0; 333 gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 334 gridBagConstraints.weightx = 1.0; 335 gridBagConstraints.insets = new java.awt.Insets (4, 4, 4, 4); 336 filePanel1.add(fileLabel1, gridBagConstraints); 337 338 jSplitPane1.setLeftComponent(filePanel1); 339 340 filePanel2.setLayout(new java.awt.GridBagLayout ()); 341 342 jEditorPane2.addCaretListener(this); 343 344 jScrollPane2.setViewportView(jEditorPane2); 345 346 gridBagConstraints = new java.awt.GridBagConstraints (); 347 gridBagConstraints.gridx = 0; 348 gridBagConstraints.gridy = 1; 349 gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; 350 gridBagConstraints.weightx = 1.0; 351 gridBagConstraints.weighty = 1.0; 352 filePanel2.add(jScrollPane2, gridBagConstraints); 353 354 fileLabel2.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); 355 fileLabel2.setLabelFor(jEditorPane2); 356 gridBagConstraints = new java.awt.GridBagConstraints (); 357 gridBagConstraints.gridx = 0; 358 gridBagConstraints.gridy = 0; 359 gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 360 gridBagConstraints.weightx = 1.0; 361 gridBagConstraints.insets = new java.awt.Insets (4, 4, 4, 4); 362 filePanel2.add(fileLabel2, gridBagConstraints); 363 364 jSplitPane1.setRightComponent(filePanel2); 365 366 add(jSplitPane1, java.awt.BorderLayout.CENTER); 367 368 } 369 370 372 public void caretUpdate(javax.swing.event.CaretEvent evt) { 373 if (evt.getSource() == jEditorPane1) { 374 DiffViewImpl.this.jEditorPane1CaretUpdate(evt); 375 } 376 else if (evt.getSource() == jEditorPane2) { 377 DiffViewImpl.this.jEditorPane2CaretUpdate(evt); 378 } 379 } 381 private void jEditorPane1CaretUpdate(javax.swing.event.CaretEvent evt) { } 385 private void jEditorPane2CaretUpdate(javax.swing.event.CaretEvent evt) { } 389 public void setCurrentLine(int line, int diffLength) { 390 if (line > 0) showLine(line, diffLength); 391 onLayoutLine = line; 392 onLayoutLength = diffLength; 393 } 394 395 private void initActions() { 396 jEditorPane1.addFocusListener(new FocusListener () { 397 public void focusGained(FocusEvent e) { 398 editorActivated(jEditorPane1); 399 } 400 public void focusLost(FocusEvent e) { 401 editorDeactivated(jEditorPane1); 402 } 403 }); 404 jEditorPane2.addFocusListener(new FocusListener () { 405 public void focusGained(FocusEvent e) { 406 editorActivated(jEditorPane2); 407 } 408 public void focusLost(FocusEvent e) { 409 editorDeactivated(jEditorPane2); 410 } 411 }); 412 } 413 414 public void addNotify() { 415 super.addNotify(); 416 417 jEditorPane1.putClientProperty("HighlightsLayerExcludes", "^org\\.netbeans\\.modules\\.editor\\.lib2\\.highlighting\\.CaretRowHighlighting$"); jEditorPane2.putClientProperty("HighlightsLayerExcludes", "^org\\.netbeans\\.modules\\.editor\\.lib2\\.highlighting\\.CaretRowHighlighting$"); 420 List<Action> actions = new ArrayList<Action>(2); 421 actions.add(getActionMap().get("jumpNext")); actions.add(getActionMap().get("jumpPrev")); jEditorPane1.setPopupActions(actions); 424 jEditorPane2.setPopupActions(actions); 425 426 expandFolds(); 427 initGlobalSizes(); 428 addChangeListeners(); 429 } 430 431 public void doLayout() { 432 super.doLayout(); 433 setCurrentLine(onLayoutLine, onLayoutLength); 434 onLayoutLine = 0; 435 } 436 437 private Hashtable<JEditorPane, Hashtable<Object , Action>> kitActions; 438 439 private PropertyChangeListener copyL; 440 private PropertyChangeListener copyP; 441 442 private Action getAction (String s, JEditorPane editor) { 443 if (kitActions == null) { 444 kitActions = new Hashtable<JEditorPane, Hashtable<Object , Action>>(); 445 } 446 Hashtable<Object , Action> actions = kitActions.get(editor); 447 if (actions == null) { 448 EditorKit kit = editor.getEditorKit(); 449 if (kit == null) { 450 return null; 451 } 452 453 Action[] a = kit.getActions (); 454 actions = new Hashtable<Object , Action>(a.length); 455 int k = a.length; 456 for (int i = 0; i < k; i++) 457 actions.put (a[i].getValue (Action.NAME), a[i]); 458 kitActions.put(editor, actions); 459 } 460 return actions.get (s); 461 } 462 463 private void editorActivated(final JEditorPane editor) { 464 final Action copy = getAction (DefaultEditorKit.copyAction, editor); 465 if (copy != null) { 466 final CallbackSystemAction sysCopy 467 = ((CallbackSystemAction) SystemAction.get (CopyAction.class)); 468 final ActionPerformer perf = new ActionPerformer () { 469 public void performAction (SystemAction action) { 470 copy.actionPerformed (new ActionEvent (editor, 0, "")); } 472 }; 473 sysCopy.setActionPerformer(copy.isEnabled() ? perf : null); 474 PropertyChangeListener copyListener; 475 copy.addPropertyChangeListener(copyListener = new PropertyChangeListener () { 476 public void propertyChange(PropertyChangeEvent evt) { 477 if ("enabled".equals(evt.getPropertyName())) { if (((Boolean )evt.getNewValue()).booleanValue()) { 479 sysCopy.setActionPerformer(perf); 480 } else if (sysCopy.getActionPerformer() == perf) { 481 sysCopy.setActionPerformer(null); 482 } 483 } 484 } 485 }); 486 if (editor.equals(jEditorPane1)) copyL = copyListener; 487 else copyP = copyListener; 488 } 489 } 490 491 private void editorDeactivated(JEditorPane editor) { 492 Action copy = getAction (DefaultEditorKit.copyAction, editor); 493 PropertyChangeListener copyListener; 494 if (editor.equals(jEditorPane1)) copyListener = copyL; 495 else copyListener = copyP; 496 if (copy != null) { 497 copy.removePropertyChangeListener(copyListener); 498 } 499 } 500 501 private void expandFolds() { 502 FoldHierarchy fh = FoldHierarchy.get(jEditorPane1); 503 FoldUtilities.expandAll(fh); 504 fh = FoldHierarchy.get(jEditorPane2); 505 FoldUtilities.expandAll(fh); 506 } 507 508 private void initGlobalSizes() { 509 StyledDocument doc1 = (StyledDocument) jEditorPane1.getDocument(); 510 StyledDocument doc2 = (StyledDocument) jEditorPane2.getDocument(); 511 int numLines1 = org.openide.text.NbDocument.findLineNumber(doc1, doc1.getEndPosition().getOffset()); 512 int numLines2 = org.openide.text.NbDocument.findLineNumber(doc2, doc2.getEndPosition().getOffset()); 513 int numLines = Math.max(numLines1, numLines2); 514 if (numLines < 1) numLines = 1; 515 this.totalLines = numLines; 516 int totHeight = jEditorPane1.getSize().height; 517 int value = jEditorPane2.getSize().height; 518 if (value > totHeight) totHeight = value; 519 this.totalHeight = totHeight; 520 } 521 522 private void showLine(int line, int diffLength) { 523 this.linesComp1.setActiveLine(line); 524 this.linesComp2.setActiveLine(line); 525 linesComp2.repaint(); 526 linesComp1.repaint(); 527 int padding = 5; 528 if (line <= 5) padding = line/2; 529 int off1, off2; 530 int ypos; 531 int viewHeight = jViewport1.getExtentSize().height; 532 java.awt.Point p1, p2; 533 initGlobalSizes(); p1 = jViewport1.getViewPosition(); 535 p2 = jViewport2.getViewPosition(); 536 ypos = (totalHeight*(line - padding - 1))/(totalLines + 1); 537 538 try { 539 off1 = org.openide.text.NbDocument.findLineOffset((StyledDocument) jEditorPane1.getDocument(), line - 1); 540 off2 = org.openide.text.NbDocument.findLineOffset((StyledDocument) jEditorPane2.getDocument(), line - 1); 541 542 jEditorPane1.setCaretPosition(off1); 543 jEditorPane2.setCaretPosition(off2); 544 } catch (IndexOutOfBoundsException ex) { 545 ErrorManager.getDefault().notify(ex); 546 } 547 548 if (ypos < p1.y || ypos + ((diffLength + padding)*totalHeight)/totalLines > p1.y + viewHeight) { 549 p1.y = ypos; 550 jViewport1.setViewPosition(p1); } 552 } 553 554 556 public void joinScrollPane(final JScrollPane pane) { 557 jScrollPane1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); 558 jScrollPane1.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 559 jScrollPane2.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); 560 jScrollPane2.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 561 jScrollPane1.setWheelScrollingEnabled(false); 562 jScrollPane2.setWheelScrollingEnabled(false); 563 jScrollPane1.addMouseWheelListener(new MouseWheelListener() { 564 public void mouseWheelMoved(MouseWheelEvent e) { 565 pane.dispatchEvent(e); 566 } 567 }); 568 jScrollPane2.addMouseWheelListener(new MouseWheelListener() { 569 public void mouseWheelMoved(MouseWheelEvent e) { 570 pane.dispatchEvent(e); 571 } 572 }); 573 jEditorPane1.getCaret().setVisible(false); 574 jEditorPane2.getCaret().setVisible(false); 575 576 KeyStroke[] keyStrokes = new KeyStroke[] { 578 KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0), 579 KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0), 580 581 KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0), 582 KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0), 583 584 KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0), 585 KeyStroke.getKeyStroke(KeyEvent.VK_HOME, InputEvent.CTRL_MASK), 586 587 KeyStroke.getKeyStroke(KeyEvent.VK_HOME, InputEvent.CTRL_MASK), 588 KeyStroke.getKeyStroke(KeyEvent.VK_HOME, InputEvent.CTRL_MASK), 589 590 KeyStroke.getKeyStroke(KeyEvent.VK_END, 0), 591 KeyStroke.getKeyStroke(KeyEvent.VK_END, InputEvent.CTRL_MASK), 592 593 KeyStroke.getKeyStroke(KeyEvent.VK_END, InputEvent.CTRL_MASK), 594 KeyStroke.getKeyStroke(KeyEvent.VK_END, InputEvent.CTRL_MASK), 595 596 KeyStroke.getKeyStroke(KeyEvent.VK_END, 0), 597 KeyStroke.getKeyStroke(KeyEvent.VK_END, 0), 598 599 KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), 600 KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), 601 602 KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), 603 KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), 604 605 KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), 606 KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), 607 608 KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), 609 KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), 610 }; 611 612 for (int i = 0; i<keyStrokes.length; i+=2) { 613 KeyStroke stroke = keyStrokes[i]; 614 KeyStroke stroke2 = keyStrokes[i+1]; 615 Object pane1Key = jEditorPane1.getInputMap().get(stroke); 616 Object pane2Key = jEditorPane2.getInputMap().get(stroke); 617 Object scrollKey = pane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).get(stroke2); 618 if (scrollKey != null) { 619 final Action scrollAction = pane.getActionMap().get(scrollKey); 620 jEditorPane1.getActionMap().put(pane1Key, new SourceTranslatorAction(scrollAction, pane)); 621 jEditorPane2.getActionMap().put(pane2Key, new SourceTranslatorAction(scrollAction, pane)); 622 } else { 623 } 625 } 626 627 } 628 629 public int getInnerScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { 630 return jEditorPane1.getScrollableUnitIncrement(visibleRect, orientation, direction); 631 } 632 633 public int getInnerScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { 634 return jEditorPane1.getScrollableBlockIncrement(visibleRect, orientation, direction); 635 } 636 637 public int getInnerWidth() { 638 Dimension d1 = jScrollPane1.getViewport().getView().getPreferredSize(); 639 Dimension d2 = jScrollPane2.getViewport().getView().getPreferredSize(); 640 int w = Math.max(d1.width, d2.width) * 2; 641 return w; 642 } 643 644 public void setInnerWidth(int width) { 645 Dimension dim = jScrollPane1.getViewport().getViewSize(); 646 dim.width = width/2; 647 jScrollPane1.getViewport().setViewSize(dim); 648 649 dim = jScrollPane2.getViewport().getViewSize(); 650 dim.width = width/2; 651 jScrollPane2.getViewport().setViewSize(dim); 652 653 jSplitPane1.setDividerLocation(0.5); 654 } 655 656 public void setHorizontalPosition(int pos) { 657 pos /= 2; 658 659 Point p = jScrollPane1.getViewport().getViewPosition(); 660 p.x = pos; 661 jScrollPane1.getViewport().setViewPosition(p); 662 663 p = jScrollPane2.getViewport().getViewPosition(); 664 p.x = pos; 665 jScrollPane2.getViewport().setViewPosition(p); 666 } 667 668 669 public int getChangeY(int change) { 670 Difference diff = diffs[change]; 671 int line = diff.getFirstStart() + diffShifts[change][0]; 672 int padding = 5; 673 if (line <= 5) { 674 padding = line/2; 675 } 676 initGlobalSizes(); 677 int ypos = (totalHeight*(line - padding - 1))/(totalLines + 1); 678 ypos += fileLabel1.getHeight(); 679 return ypos; 680 } 681 682 683 private void joinScrollBars() { 684 final JScrollBar scrollBarH1 = jScrollPane1.getHorizontalScrollBar(); 685 final JScrollBar scrollBarV1 = jScrollPane1.getVerticalScrollBar(); 686 final JScrollBar scrollBarH2 = jScrollPane2.getHorizontalScrollBar(); 687 final JScrollBar scrollBarV2 = jScrollPane2.getVerticalScrollBar(); 688 scrollBarV1.getModel().addChangeListener(new javax.swing.event.ChangeListener () { 689 public void stateChanged(javax.swing.event.ChangeEvent e) { 690 int value = scrollBarV1.getValue(); 691 int oldValue = scrollBarV2.getValue(); 692 if (oldValue != value) { 693 scrollBarV2.setValue(value); 694 } 695 } 696 }); 697 jScrollPane1.getVerticalScrollBar().setPreferredSize(new Dimension (0, 0)); 700 scrollBarV2.getModel().addChangeListener(new javax.swing.event.ChangeListener () { 701 public void stateChanged(javax.swing.event.ChangeEvent e) { 702 int value = scrollBarV2.getValue(); 703 int oldValue = scrollBarV1.getValue(); 704 if (oldValue != value) { 705 scrollBarV1.setValue(value); 706 } 707 } 708 }); 709 scrollBarH1.getModel().addChangeListener(new javax.swing.event.ChangeListener () { 710 public void stateChanged(javax.swing.event.ChangeEvent e) { 711 int value = scrollBarH1.getValue(); 712 if (value == horizontalScroll1ChangedValue) return; 713 int max1 = scrollBarH1.getMaximum(); 714 int max2 = scrollBarH2.getMaximum(); 715 int ext1 = scrollBarH1.getModel().getExtent(); 716 int ext2 = scrollBarH2.getModel().getExtent(); 717 if (max1 == ext1) horizontalScroll2ChangedValue = 0; 718 else horizontalScroll2ChangedValue = (value*(max2 - ext2))/(max1 - ext1); 719 horizontalScroll1ChangedValue = -1; 720 scrollBarH2.setValue(horizontalScroll2ChangedValue); 721 } 722 }); 723 scrollBarH2.getModel().addChangeListener(new javax.swing.event.ChangeListener () { 724 public void stateChanged(javax.swing.event.ChangeEvent e) { 725 int value = scrollBarH2.getValue(); 726 if (value == horizontalScroll2ChangedValue) return; 727 int max1 = scrollBarH1.getMaximum(); 728 int max2 = scrollBarH2.getMaximum(); 729 int ext1 = scrollBarH1.getModel().getExtent(); 730 int ext2 = scrollBarH2.getModel().getExtent(); 731 if (max2 == ext2) horizontalScroll1ChangedValue = 0; 732 else horizontalScroll1ChangedValue = (value*(max1 - ext1))/(max2 - ext2); 733 horizontalScroll2ChangedValue = -1; 734 scrollBarH1.setValue(horizontalScroll1ChangedValue); 735 } 736 }); 737 jSplitPane1.setDividerLocation(0.5); 738 } 739 740 private String strCharacters(char c, int num) { 741 StringBuffer s = new StringBuffer (); 742 while(num-- > 0) { 743 s.append(c); 744 } 745 return s.toString(); 746 } 747 748 private void customizeEditor(JEditorPane editor) { 749 StyledDocument doc; 750 Document document = editor.getDocument(); 751 try { 752 doc = (StyledDocument) editor.getDocument(); 753 } catch(ClassCastException e) { 754 doc = new DefaultStyledDocument(); 755 try { 756 doc.insertString(0, document.getText(0, document.getLength()), null); 757 } catch (BadLocationException ble) { 758 } 760 editor.setDocument(doc); 761 } 762 } 763 764 private void addChangeListeners() { 765 jEditorPane1.addPropertyChangeListener("font", new java.beans.PropertyChangeListener () { 766 public void propertyChange(java.beans.PropertyChangeEvent evt) { 767 javax.swing.SwingUtilities.invokeLater(new Runnable () { 768 public void run() { 769 initGlobalSizes(); 770 linesComp1.repaint(); 771 } 772 }); 773 } 774 }); 775 jEditorPane2.addPropertyChangeListener("font", new java.beans.PropertyChangeListener () { 776 public void propertyChange(java.beans.PropertyChangeEvent evt) { 777 javax.swing.SwingUtilities.invokeLater(new Runnable () { 778 public void run() { 779 initGlobalSizes(); 780 linesComp2.repaint(); 781 } 782 }); 783 } 784 }); 785 } 786 787 public void setSource1(Reader r) throws IOException { 788 EditorKit kit = jEditorPane1.getEditorKit(); 789 if (kit == null) throw new IOException("Missing Editor Kit"); Document doc = kit.createDefaultDocument(); 791 if (!(doc instanceof StyledDocument)) { 792 doc = new DefaultStyledDocument(new StyleContext()); 793 kit = new StyledEditorKit(); 794 jEditorPane1.setEditorKit(kit); 795 } 796 try { 797 kit.read(r, doc, 0); 798 } catch (javax.swing.text.BadLocationException e) { 799 throw new IOException("Can not locate the beginning of the document."); } finally { 801 r.close(); 802 } 803 kit.install(jEditorPane1); 804 jEditorPane1.setDocument(doc); 805 jEditorPane1.setEditable(false); 806 customizeEditor(jEditorPane1); 807 linesComp1 = new LinesComponent(jEditorPane1); 808 jScrollPane1.setRowHeaderView(linesComp1); 809 jViewport1 = jScrollPane1.getViewport(); 810 } 811 812 public void setSource2(Reader r) throws IOException { 813 EditorKit kit = jEditorPane2.getEditorKit(); 814 if (kit == null) throw new IOException("Missing Editor Kit"); Document doc = kit.createDefaultDocument(); 816 if (!(doc instanceof StyledDocument)) { 817 doc = new DefaultStyledDocument(new StyleContext()); 818 kit = new StyledEditorKit(); 819 jEditorPane2.setEditorKit(kit); 820 } 821 try { 822 kit.read(r, doc, 0); 823 } catch (javax.swing.text.BadLocationException e) { 824 throw new IOException("Can not locate the beginning of the document."); } finally { 826 r.close(); 827 } 828 kit.install(jEditorPane2); 829 jEditorPane2.setDocument(doc); 830 jEditorPane2.setEditable(false); 831 832 customizeEditor(jEditorPane2); 833 linesComp2 = new LinesComponent(jEditorPane2); 834 jScrollPane2.setRowHeaderView(linesComp2); 835 jViewport2 = jScrollPane2.getViewport(); 836 joinScrollBars(); 838 } 839 840 public void setSource1Title(String title) { 841 fileLabel1.setText(title); 842 fileLabel1.setMinimumSize(new Dimension (3, fileLabel1.getMinimumSize().height)); 844 } 845 846 public void setSource2Title(String title) { 847 fileLabel2.setText(title); 848 fileLabel2.setMinimumSize(new Dimension (3, fileLabel2.getMinimumSize().height)); 850 } 851 852 public void setMimeType1(String mime) { 853 EditorKit kit = CloneableEditorSupport.getEditorKit(mime); 854 jEditorPane1.setEditorKit(kit); 855 } 856 857 public void setMimeType2(String mime) { 858 EditorKit kit = CloneableEditorSupport.getEditorKit(mime); 859 jEditorPane2.setEditorKit(kit); 860 } 861 862 public void setDocument1(Document doc) { 863 if (doc != null) { 864 jEditorPane1.setDocument(doc); 865 } 866 } 867 868 public void setDocument2(Document doc) { 869 if (doc != null) { 870 jEditorPane2.setDocument(doc); 871 } 872 } 873 874 String getDocumentText1() { 875 return jEditorPane1.getText(); 876 } 877 878 String getDocumentText2() { 879 return jEditorPane2.getText(); 880 } 881 882 private void setHighlight(StyledDocument doc, int line1, int line2, java.awt.Color color) { 883 for(int line = line1-1; line < line2; line++) { 884 if (line < 0) continue; 885 int offset = org.openide.text.NbDocument.findLineOffset(doc, line); 886 if (offset >= 0) { 887 Style s = doc.getLogicalStyle(offset); 888 if (s == null) { 889 s = doc.addStyle("diff-style("+color+"):1500", null); } 891 s.addAttribute(StyleConstants.ColorConstants.Background, color); 892 doc.setLogicalStyle(offset, s); 893 } 894 } 895 } 896 897 private void unhighlight(StyledDocument doc) { 898 int endOffset = doc.getEndPosition().getOffset(); 899 int endLine = org.openide.text.NbDocument.findLineNumber(doc, endOffset); 900 Style s = doc.addStyle("diff-style(white):1500", null); s.addAttribute(StyleConstants.ColorConstants.Background, java.awt.Color.white); 902 for(int line = 0; line <= endLine; line++) { 903 int offset = org.openide.text.NbDocument.findLineOffset(doc, line); 904 doc.setLogicalStyle(offset, s); 905 } 906 } 907 908 public void unhighlightAll() { 909 unhighlight((StyledDocument) jEditorPane1.getDocument()); 910 unhighlight((StyledDocument) jEditorPane2.getDocument()); 911 } 912 913 public void highlightRegion1(int line1, int line2, java.awt.Color color) { 914 StyledDocument doc = (StyledDocument) jEditorPane1.getDocument(); 915 setHighlight(doc, line1, line2, color); 916 } 917 918 public void highlightRegion2(int line1, int line2, java.awt.Color color) { 919 StyledDocument doc = (StyledDocument) jEditorPane2.getDocument(); 920 setHighlight(doc, line1, line2, color); 921 } 922 923 private void addEmptyLines(StyledDocument doc, int line, int numLines) { 924 int lastOffset = doc.getEndPosition().getOffset(); 925 int totLines = org.openide.text.NbDocument.findLineNumber(doc, lastOffset); 926 int offset = lastOffset-1; 927 if (line <= totLines) { 928 offset = org.openide.text.NbDocument.findLineOffset(doc, line); 929 } 930 String insStr = strCharacters('\n', numLines); 931 try { 932 doc.insertString(offset, insStr, null); 933 } catch (BadLocationException e) { 934 org.openide.ErrorManager.getDefault().notify(e); 935 } 936 } 937 938 public void addEmptyLines1(int line, int numLines) { 939 StyledDocument doc = (StyledDocument) jEditorPane1.getDocument(); 940 addEmptyLines(doc, line, numLines); 941 linesComp1.addEmptyLines(line, numLines); 942 } 943 944 public void addEmptyLines2(int line, int numLines) { 945 StyledDocument doc = (StyledDocument) jEditorPane2.getDocument(); 946 addEmptyLines(doc, line, numLines); 947 linesComp2.addEmptyLines(line, numLines); 948 } 949 950 951 private javax.swing.JViewport jViewport1; 952 private javax.swing.JViewport jViewport2; 953 954 final javax.swing.JLabel fileLabel1 = new javax.swing.JLabel (); 956 final javax.swing.JLabel fileLabel2 = new javax.swing.JLabel (); 957 final javax.swing.JPanel filePanel1 = new javax.swing.JPanel (); 958 final javax.swing.JPanel filePanel2 = new javax.swing.JPanel (); 959 final org.netbeans.modules.diff.builtin.visualizer.DEditorPane jEditorPane1 = new DEditorPane(); 960 final org.netbeans.modules.diff.builtin.visualizer.DEditorPane jEditorPane2 = new DEditorPane(); 961 final javax.swing.JScrollPane jScrollPane1 = new javax.swing.JScrollPane (); 962 final javax.swing.JScrollPane jScrollPane2 = new javax.swing.JScrollPane (); 963 final javax.swing.JSplitPane jSplitPane1 = new javax.swing.JSplitPane (); 964 966 967 968 private void insertEmptyLines(boolean updateActionLines) { 969 int n = diffs.length; 970 for(int i = 0; i < n; i++) { 974 Difference action = diffs[i]; 975 int n1 = action.getFirstStart() + diffShifts[i][0]; 976 int n2 = action.getFirstEnd() + diffShifts[i][0]; 977 int n3 = action.getSecondStart() + diffShifts[i][1]; 978 int n4 = action.getSecondEnd() + diffShifts[i][1]; 979 if (updateActionLines && i < n - 1) { 982 diffShifts[i + 1][0] = diffShifts[i][0]; 983 diffShifts[i + 1][1] = diffShifts[i][1]; 984 } 985 switch (action.getType()) { 986 case Difference.DELETE: 987 addEmptyLines2(n3, n2 - n1 + 1); 988 if (updateActionLines && i < n - 1) { 989 diffShifts[i+1][1] += n2 - n1 + 1; 990 } 991 break; 993 case Difference.ADD: 994 addEmptyLines1(n1, n4 - n3 + 1); 995 if (updateActionLines && i < n - 1) { 996 diffShifts[i+1][0] += n4 - n3 + 1; 997 } 998 break; 1000 case Difference.CHANGE: 1001 int r1 = n2 - n1; 1002 int r2 = n4 - n3; 1003 if (r1 < r2) { 1004 addEmptyLines1(n2, r2 - r1); 1005 if (updateActionLines && i < n - 1) { 1006 diffShifts[i+1][0] += r2 - r1; 1007 } 1008 } else if (r1 > r2) { 1010 addEmptyLines2(n4, r1 - r2); 1011 if (updateActionLines && i < n - 1) { 1012 diffShifts[i+1][1] += r1 - r2; 1013 } 1014 } 1016 break; 1017 } 1018 } 1019 } 1020 1021 private void setDiffHighlight(boolean set) { 1022 int n = diffs.length; 1023 for(int i = 0; i < n; i++) { 1025 Difference action = diffs[i]; 1026 int n1 = action.getFirstStart() + diffShifts[i][0]; 1027 int n2 = action.getFirstEnd() + diffShifts[i][0]; 1028 int n3 = action.getSecondStart() + diffShifts[i][1]; 1029 int n4 = action.getSecondEnd() + diffShifts[i][1]; 1030 switch (action.getType()) { 1032 case Difference.DELETE: 1033 if (set) highlightRegion1(n1, n2, colorMissing); 1034 else highlightRegion1(n1, n2, java.awt.Color.white); 1035 break; 1036 case Difference.ADD: 1037 if (set) highlightRegion2(n3, n4, colorAdded); 1038 else highlightRegion2(n3, n4, java.awt.Color.white); 1039 break; 1040 case Difference.CHANGE: 1041 if (set) { 1042 highlightRegion1(n1, n2, colorChanged); 1043 highlightRegion2(n3, n4, colorChanged); 1044 } else { 1045 highlightRegion1(n1, n2, java.awt.Color.white); 1046 highlightRegion2(n3, n4, java.awt.Color.white); 1047 } 1048 break; 1049 } 1050 } 1051 } 1052 1053 1069 private void insertEmptyLinesNotReported() { 1070 String docText1 = getDocumentText1(); 1071 String docText2 = getDocumentText2(); 1072 int[] begin1 = { 0 }; 1073 int[] end1 = { -1 }; 1074 int[] begin2 = { 0 }; 1075 int[] end2 = { -1 }; 1076 int n1 = docText1.length(); 1077 int n2 = docText2.length(); 1078 int lineNumber = 1; 1079 int diffIndex = 0; 1080 do { 1081 int lastBegin1 = begin1[0]; 1082 int lastBegin2 = begin2[0]; 1083 String line1 = readLine(begin1, end1, docText1); 1084 String line2 = readLine(begin2, end2, docText2); 1085 if (line1.length() == 0 && line2.length() > 0) { 1086 diffIndex = findDiffForLine(lineNumber, diffIndex, diffs, diffShifts); 1088 if (diffIndex >= diffs.length || !isLineInDiff(lineNumber, diffs[diffIndex], diffShifts[diffIndex])) { 1089 boolean addMissingLine; 1090 if (line2.trim().length() == 0) { 1091 int emptyLines1 = numEmptyLines(begin1[0], docText1, (diffIndex < diffs.length) ? diffs[diffIndex].getFirstStart() : -1); 1092 int emptyLines2 = numEmptyLines(begin2[0], docText2, (diffIndex < diffs.length) ? diffs[diffIndex].getSecondStart() : -1); 1093 addMissingLine = emptyLines1 > emptyLines2; 1094 } else { 1096 addMissingLine = true; 1097 } 1098 if (addMissingLine) { 1099 addEmptyLines2(lineNumber - 1, 1); 1100 shiftDiffs(false, lineNumber); 1102 begin2[0] = lastBegin2; 1103 end2[0] = lastBegin2 - 1; 1104 } 1105 } 1106 } else if (line2.length() == 0 && line1.length() > 0) { 1107 diffIndex = findDiffForLine(lineNumber, diffIndex, diffs, diffShifts); 1109 if (diffIndex >= diffs.length || !isLineInDiff(lineNumber, diffs[diffIndex], diffShifts[diffIndex])) { 1110 boolean addMissingLine; 1111 if (line1.trim().length() == 0) { 1112 int emptyLines1 = numEmptyLines(begin1[0], docText1, (diffIndex < diffs.length) ? diffs[diffIndex].getFirstStart() : -1); 1113 int emptyLines2 = numEmptyLines(begin2[0], docText2, (diffIndex < diffs.length) ? diffs[diffIndex].getSecondStart() : -1); 1114 addMissingLine = emptyLines2 > emptyLines1; 1115 } else { 1117 addMissingLine = true; 1118 } 1119 if (addMissingLine) { 1120 addEmptyLines1(lineNumber - 1, 1); 1121 shiftDiffs(true, lineNumber); 1123 begin1[0] = lastBegin1; 1124 end1[0] = lastBegin1 - 1; 1125 } 1126 } 1127 } 1128 lineNumber++; 1129 } while (begin1[0] < n1 && begin2[0] < n2); 1130 } 1131 1132 1137 private void shiftDiffs(boolean inFirstDoc, int fromLine) { 1138 int n = diffs.length; 1139 for(int i = 0; i < n; i++) { 1140 Difference action = diffs[i]; 1141 if (inFirstDoc) { 1142 if (action.getFirstStart() + diffShifts[i][0] >= fromLine) { 1143 diffShifts[i][0]++; 1144 } 1145 } else { 1146 if (action.getSecondStart() + diffShifts[i][1] >= fromLine) { 1147 diffShifts[i][1]++; 1148 } 1149 } 1150 } 1151 } 1152 1153 private static int numEmptyLines(int beginLine, String text, int endLine) { 1154 if (endLine >= 0 && endLine <= beginLine) return 0; 1155 int numLines = 0; 1156 int[] begin = { beginLine }; 1157 int[] end = { 0 }; 1158 do { 1159 String line = readLine(begin, end, text); 1160 if (line.trim().length() > 0) break; 1161 numLines++; 1162 } while ((endLine < 0 || beginLine + numLines < endLine) && begin[0] < text.length()); 1163 return numLines; 1164 } 1165 1166 1172 private static int findDiffForLine(int lineNumber, int diffIndex, Difference[] diffs, int[][] diffShifts) { 1173 while (diffIndex < diffs.length) { 1174 if ((diffs[diffIndex].getFirstEnd() + diffShifts[diffIndex][0]) >= lineNumber || 1175 (diffs[diffIndex].getSecondEnd() + diffShifts[diffIndex][1]) >= lineNumber) break; 1176 diffIndex++; 1177 } 1178 return diffIndex; 1179 } 1180 1181 1188 private static boolean isLineInDiff(int lineNumber, Difference diff, int[] diffShifts) { 1189 int l1 = diff.getFirstStart() + diffShifts[0]; 1190 int l2 = diff.getFirstEnd() + diffShifts[0]; 1191 int l3 = diff.getSecondStart() + diffShifts[1]; 1192 int l4 = diff.getSecondEnd() + diffShifts[1]; 1193 return (l1 <= lineNumber && ((l2 >= l1) ? (l2 >= lineNumber) : false)) || 1194 (l3 <= lineNumber && ((l4 >= l3) ? (l4 >= lineNumber) : false)); 1195 } 1196 1197 1206 private static String readLine(int[] begin, int[] end, String text) { 1207 int n = text.length(); 1208 for (int i = begin[0]; i < n; i++) { 1209 char c = text.charAt(i); 1210 if (c == '\n' || c == '\r') { 1211 end[0] = i; 1212 break; 1213 } 1214 } 1215 if (end[0] < begin[0]) end[0] = n; 1216 String line = text.substring(begin[0], end[0]); 1217 begin[0] = end[0] + 1; 1218 if (begin[0] < n && text.charAt(end[0]) == '\r' && text.charAt(begin[0]) == '\n') begin[0]++; 1219 return line; 1220 } 1221 1222} 1223 | Popular Tags |