1 19 20 package org.netbeans.editor; 21 22 import java.awt.*; 23 import java.awt.event.FocusAdapter ; 24 import java.awt.event.FocusEvent ; 25 import java.util.Hashtable ; 26 import java.util.Map ; 27 import java.util.HashMap ; 28 import java.util.Iterator ; 29 import java.beans.PropertyChangeListener ; 30 import java.beans.PropertyChangeEvent ; 31 import java.beans.PropertyChangeSupport ; 32 import javax.swing.Action ; 33 import javax.swing.JComponent ; 34 import javax.swing.JViewport ; 35 import javax.swing.JPanel ; 36 import javax.swing.JScrollPane ; 37 import javax.swing.JToolBar ; 38 import javax.swing.SwingUtilities ; 39 import javax.swing.event.ChangeListener ; 40 import javax.swing.event.ChangeEvent ; 41 import javax.swing.text.JTextComponent ; 42 import javax.swing.text.Caret ; 43 import javax.swing.text.BadLocationException ; 44 import javax.swing.text.Position ; 45 import javax.swing.text.View ; 46 import javax.swing.plaf.TextUI ; 47 import org.netbeans.modules.editor.lib.HighlightingDrawLayer; 48 49 57 public class EditorUI implements ChangeListener , PropertyChangeListener , SettingsChangeListener { 58 59 public static final String OVERWRITE_MODE_PROPERTY = "overwriteMode"; 61 public static final String COMPONENT_PROPERTY = "component"; 63 69 public static final int SCROLL_DEFAULT = 0; 70 71 74 public static final int SCROLL_MOVE = 1; 75 76 80 public static final int SCROLL_SMALLEST = 2; 81 82 86 public static final int SCROLL_FIND = 3; 87 88 89 private static final Insets NULL_INSETS = new Insets(0, 0, 0, 0); 90 91 private static final Insets DEFAULT_INSETS = new Insets(0, SettingsDefaults.defaultTextLeftMarginWidth.intValue(), 0, 0); 92 93 private static final Dimension NULL_DIMENSION = new Dimension(0, 0); 94 95 96 public static final Insets defaultLineNumberMargin = new Insets(0, 3, 0, 3); 97 98 private static final boolean debugUpdateLineHeight 99 = Boolean.getBoolean("netbeans.debug.editor.updateLineHeight"); 100 101 105 private static final HashMap sharedColoringMaps = new HashMap (57); 106 private static final SettingsChangeListener clearingListener 107 = new SettingsChangeListener() { 108 public void settingsChange(SettingsChangeEvent evt) { 109 sharedColoringMaps.clear(); 111 } 112 }; 113 114 115 static { 116 Settings.addSettingsChangeListener( clearingListener ); 117 } 118 119 120 121 private JTextComponent component; 122 123 private JComponent extComponent; 124 125 private JToolBar toolBarComponent; 126 127 128 PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport (this); 129 130 131 private BaseDocument printDoc; 132 133 134 private DrawLayerList drawLayerList = new DrawLayerList(); 135 136 137 private Map coloringMap; 138 139 142 private int lineHeight = 1; 144 private float lineHeightCorrection = 1.0f; 145 146 147 private int lineAscent; 148 149 150 int defaultSpaceWidth = 1; 151 152 153 boolean highlightSearch; 154 155 159 boolean lineNumberEnabled; 160 161 162 boolean lineNumberVisibleSetting; 163 164 167 boolean lineNumberVisible; 168 169 172 int lineNumberWidth; 173 174 177 int lineNumberDigitWidth; 178 179 180 int lineNumberMaxDigitCount; 181 182 183 int textLeftMarginWidth; 184 185 189 Insets textMargin = DEFAULT_INSETS; 190 191 195 Insets scrollJumpInsets; 196 197 201 Insets scrollFindInsets; 202 203 204 Hashtable props = new Hashtable (11); 205 206 boolean textLimitLineVisible; 207 208 Color textLimitLineColor; 209 210 int textLimitWidth; 211 212 private Rectangle lastExtentBounds = new Rectangle(); 213 214 private Dimension componentSizeIncrement = new Dimension(); 215 216 private Abbrev abbrev; 217 218 private WordMatch wordMatch; 219 220 private Object componentLock; 221 222 223 StatusBar statusBar; 224 225 private FocusAdapter focusL; 226 227 Map renderingHints; 228 229 230 private GlyphGutter glyphGutter = null; 231 232 235 private boolean disableLineNumbers = true; 236 237 238 private JPanel glyphCorner; 239 240 241 private boolean usePrintColoringMap; 242 243 public static final String LINE_HEIGHT_CHANGED_PROP = "line-height-changed-prop"; 245 246 private static boolean isPasteActionInited = false; 247 248 249 public EditorUI() { 250 this.usePrintColoringMap = false; 251 252 Settings.addSettingsChangeListener(this); 253 254 focusL = new FocusAdapter () { 255 public void focusGained(FocusEvent evt) { 256 Registry.activate(getComponent()); 257 260 stateChanged(null); 261 if (component!=null){ 262 BaseTextUI ui = (BaseTextUI)component.getUI(); 263 if (ui!=null) ui.refresh(); 264 } 265 } 266 }; 267 268 HighlightingDrawLayer.hookUp(this); 269 } 270 271 272 public EditorUI(BaseDocument printDoc) { 273 this(printDoc, true, true); 274 } 275 276 286 public EditorUI(BaseDocument printDoc, boolean usePrintColoringMap, boolean lineNumberEnabled) { 287 this.printDoc = printDoc; 288 this.usePrintColoringMap = usePrintColoringMap; 289 290 settingsChange(null); 291 292 setLineNumberEnabled(lineNumberEnabled); 293 294 updateLineNumberWidth(0); 295 296 drawLayerList.add(printDoc.getDrawLayerList()); 297 HighlightingDrawLayer.hookUp(this); 298 } 299 300 303 protected static Map getSharedColoringMap(Class kitClass) { 304 synchronized (Settings.class) { Map cm = (Map )sharedColoringMaps.get(kitClass); 306 if (cm == null) { 307 cm = SettingsUtil.getColoringMap(kitClass, false, true); 308 if (cm.get(SettingsNames.DEFAULT_COLORING) == null) { 310 cm.put(SettingsNames.DEFAULT_COLORING, SettingsDefaults.defaultColoring); 311 } 312 313 sharedColoringMaps.put(kitClass, cm); 314 } 315 316 return cm; 317 } 318 } 319 320 324 void initLineHeight(JTextComponent c) { 325 updateLineHeight(c); 327 } 328 329 332 protected void installUI(JTextComponent c) { 333 synchronized (getComponentLock()) { 334 this.component = c; 335 336 putProperty(COMPONENT_PROPERTY, c); 337 338 component.addPropertyChangeListener(this); 340 component.addFocusListener(focusL); 341 342 Caret caret = component.getCaret(); 344 if (caret != null) { 345 caret.addChangeListener(this); 346 } 347 348 BaseDocument doc = getDocument(); 349 if (doc != null) { 350 modelChanged(null, doc); 351 } 352 } 353 354 settingsChange(null); 356 357 getDefaultColoring().apply(component); 359 360 component.setDragEnabled(true); 362 } 363 364 367 protected void uninstallUI(JTextComponent c) { 368 synchronized (getComponentLock()) { 369 370 if (component != null) { 372 373 Caret caret = component.getCaret(); 375 if (caret != null) { 376 caret.removeChangeListener(this); 377 } 378 379 component.removePropertyChangeListener(this); 381 component.removeFocusListener(focusL); 382 383 } 384 385 BaseDocument doc = getDocument(); 386 if (doc != null) { 387 modelChanged(doc, null); 388 } 389 390 component = null; 391 putProperty(COMPONENT_PROPERTY, null); 392 393 FontMetricsCache.clear(); 395 } 396 } 397 398 403 public Object getComponentLock() { 404 if (componentLock == null) { 405 componentLock = new ComponentLock(); 406 } 407 return componentLock; 408 } 409 static class ComponentLock {}; 410 411 public void addPropertyChangeListener(PropertyChangeListener l) { 412 propertyChangeSupport.addPropertyChangeListener(l); 413 } 414 415 public void addPropertyChangeListener(String propertyName, PropertyChangeListener l) { 416 propertyChangeSupport.addPropertyChangeListener(propertyName, l); 417 } 418 419 public void removePropertyChangeListener(PropertyChangeListener l) { 420 propertyChangeSupport.removePropertyChangeListener(l); 421 } 422 423 public void removePropertyChangeListener(String propertyName, PropertyChangeListener l) { 424 propertyChangeSupport.removePropertyChangeListener(propertyName, l); 425 } 426 427 protected final void firePropertyChange(String propertyName, Object oldValue, Object newValue) { 428 propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue); 429 } 430 431 protected void settingsChangeImpl(String settingName){ 432 Class kitClass = getKitClass(); 433 434 coloringMap = null; 436 if (settingName == null || SettingsNames.LINE_NUMBER_VISIBLE.equals(settingName)) { 437 lineNumberVisibleSetting = SettingsUtil.getBoolean(kitClass, 438 SettingsNames.LINE_NUMBER_VISIBLE, 439 SettingsDefaults.defaultLineNumberVisible); 440 lineNumberVisible = lineNumberEnabled && lineNumberVisibleSetting; 441 442 if (component == null) 444 disableLineNumbers = false; 445 446 if (disableLineNumbers) 447 lineNumberVisible = false; 448 } 449 450 BaseDocument doc = getDocument(); 451 if (doc != null) { 452 453 if (settingName == null || SettingsNames.TEXT_LEFT_MARGIN_WIDTH.equals(settingName)) { 454 textLeftMarginWidth = SettingsUtil.getInteger(kitClass, 455 SettingsNames.TEXT_LEFT_MARGIN_WIDTH, 456 SettingsDefaults.defaultTextLeftMarginWidth); 457 } 458 459 if (settingName == null || SettingsNames.LINE_HEIGHT_CORRECTION.equals(settingName)) { 460 Object value = Settings.getValue(kitClass, SettingsNames.LINE_HEIGHT_CORRECTION); 461 if (!(value instanceof Float ) || ((Float )value).floatValue() < 0) { 462 value = SettingsDefaults.defaultLineHeightCorrection; 463 } 464 465 float newLineHeightCorrection = ((Float )value).floatValue(); 466 if (newLineHeightCorrection != lineHeightCorrection){ 467 lineHeightCorrection = newLineHeightCorrection; 468 updateLineHeight(getComponent()); 469 } 470 } 471 472 if (settingName == null || SettingsNames.TEXT_LIMIT_LINE_VISIBLE.equals(settingName)) { 473 textLimitLineVisible = SettingsUtil.getBoolean(kitClass, 474 SettingsNames.TEXT_LIMIT_LINE_VISIBLE, SettingsDefaults.defaultTextLimitLineVisible); 475 } 476 477 if (settingName == null || SettingsNames.TEXT_LIMIT_LINE_COLOR.equals(settingName)) { 478 Object value = Settings.getValue(kitClass, SettingsNames.TEXT_LIMIT_LINE_COLOR); 479 textLimitLineColor = (value instanceof Color) ? (Color)value 480 : SettingsDefaults.defaultTextLimitLineColor; 481 } 482 483 if (settingName == null || SettingsNames.TEXT_LIMIT_WIDTH.equals(settingName)) { 484 textLimitWidth = SettingsUtil.getPositiveInteger(kitClass, 485 SettingsNames.TEXT_LIMIT_WIDTH, SettingsDefaults.defaultTextLimitWidth); 486 } 487 488 if (component != null) { 490 if (settingName == null || SettingsNames.SCROLL_JUMP_INSETS.equals(settingName)) { 491 Object value = Settings.getValue(kitClass, SettingsNames.SCROLL_JUMP_INSETS); 492 scrollJumpInsets = (value instanceof Insets) ? (Insets)value : NULL_INSETS; 493 } 494 495 if (settingName == null || SettingsNames.SCROLL_FIND_INSETS.equals(settingName)) { 496 Object value = Settings.getValue(kitClass, SettingsNames.SCROLL_FIND_INSETS); 497 scrollFindInsets = (value instanceof Insets) ? (Insets)value : NULL_INSETS; 498 } 499 500 if (settingName == null || SettingsNames.COMPONENT_SIZE_INCREMENT.equals(settingName)) { 501 Object value = Settings.getValue(kitClass, SettingsNames.COMPONENT_SIZE_INCREMENT); 502 componentSizeIncrement = (value instanceof Dimension) ? (Dimension)value : NULL_DIMENSION; 503 } 504 505 if (settingName == null || SettingsNames.RENDERING_HINTS.equals(settingName)) { 506 507 Object userSetHints = Settings.getValue(kitClass, SettingsNames.RENDERING_HINTS); 509 renderingHints = (userSetHints instanceof Map && ((Map )userSetHints).size() > 0) ? (Map )userSetHints : null; 510 } 511 512 if (settingName == null || SettingsNames.CARET_COLOR_INSERT_MODE.equals(settingName) 513 || SettingsNames.CARET_COLOR_OVERWRITE_MODE.equals(settingName) 514 ) { 515 Boolean b = (Boolean )getProperty(OVERWRITE_MODE_PROPERTY); 516 Color caretColor; 517 if (b == null || !b.booleanValue()) { 518 Object value = Settings.getValue(kitClass, SettingsNames.CARET_COLOR_INSERT_MODE); 519 caretColor = (value instanceof Color) ? (Color)value 520 : SettingsDefaults.defaultCaretColorInsertMode; 521 522 } else { 523 Object value = Settings.getValue(kitClass, SettingsNames.CARET_COLOR_OVERWRITE_MODE); 524 caretColor = (value instanceof Color) ? (Color)value 525 : SettingsDefaults.defaultCaretColorOvwerwriteMode; 526 } 527 528 if (caretColor != null) { 529 component.setCaretColor(caretColor); 530 } 531 } 532 533 534 Utilities.runInEventDispatchThread( 535 new Runnable () { 536 public void run() { 537 JTextComponent c = component; 538 if (c != null) { 539 BaseKit kit = Utilities.getKit(c); 540 if (kit != null) { 541 c.setKeymap(kit.getKeymap()); 542 updateComponentProperties(); 543 544 ((BaseTextUI)c.getUI()).preferenceChanged(true, true); 545 } 546 } 547 } 548 } 549 ); 550 } 551 } 552 } 553 554 public void settingsChange(SettingsChangeEvent evt) { 555 if (component != null) { 556 if (Utilities.getKit(component) == null) { 557 return; } 559 } 560 String settingName = (evt != null) ? evt.getSettingName() : null; 561 settingsChangeImpl(settingName); 562 } 563 564 public void stateChanged(ChangeEvent evt) { 565 SwingUtilities.invokeLater( 566 new Runnable () { 567 568 572 private boolean isCaretGuarded(){ 573 JTextComponent c = component; 574 BaseDocument bdoc = getDocument(); 575 boolean inGuardedBlock = false; 576 if (bdoc instanceof GuardedDocument){ 577 GuardedDocument gdoc = (GuardedDocument)bdoc; 578 579 boolean selectionSpansGuardedSection = false; 580 for (int i=c.getSelectionStart(); i<c.getSelectionEnd(); i++){ 581 if (gdoc.isPosGuarded(i)){ 582 selectionSpansGuardedSection = true; 583 break; 584 } 585 } 586 587 inGuardedBlock = (gdoc.isPosGuarded(c.getCaretPosition()) || 588 selectionSpansGuardedSection); 589 } 590 return inGuardedBlock; 591 } 592 593 public void run() { 594 JTextComponent c = component; 595 if (c != null) { 596 BaseKit kit = Utilities.getKit(c); 597 if (kit != null) { 598 boolean isEditable = c.isEditable(); 599 boolean selectionVisible = c.getCaret().isSelectionVisible(); 600 boolean caretGuarded = isCaretGuarded(); 601 602 Action a = kit.getActionByName(BaseKit.copyAction); 603 if (a != null) { 604 a.setEnabled(selectionVisible); 605 } 606 607 a = kit.getActionByName(BaseKit.cutAction); 608 if (a != null) { 609 a.setEnabled(selectionVisible && !caretGuarded && isEditable); 610 } 611 612 a = kit.getActionByName(BaseKit.removeSelectionAction); 613 if (a != null) { 614 a.setEnabled(selectionVisible && !caretGuarded && isEditable); 615 } 616 617 a = kit.getActionByName(BaseKit.pasteAction); 618 if (a != null) { 619 if (!isPasteActionInited) { 620 a.setEnabled(!a.isEnabled()); 622 isPasteActionInited = true; 623 } 624 a.setEnabled(!caretGuarded && isEditable); 625 } 626 } 627 } 628 } 629 } 630 ); 631 } 632 633 protected void modelChanged(BaseDocument oldDoc, BaseDocument newDoc) { 634 if (oldDoc != null) { 635 drawLayerList.remove(oldDoc.getDrawLayerList()); 637 } 638 639 if (newDoc != null) { 640 settingsChange(null); 641 642 drawLayerList.add(newDoc.getDrawLayerList()); 644 } 645 } 646 647 public void propertyChange(PropertyChangeEvent evt) { 648 String propName = evt.getPropertyName(); 649 650 if ("document".equals(propName)) { BaseDocument oldDoc = (evt.getOldValue() instanceof BaseDocument) 652 ? (BaseDocument)evt.getOldValue() : null; 653 BaseDocument newDoc = (evt.getNewValue() instanceof BaseDocument) 654 ? (BaseDocument)evt.getNewValue() : null; 655 modelChanged(oldDoc, newDoc); 656 657 } else if ("margin".equals(propName)) { updateTextMargin(); 659 660 } else if ("caret".equals(propName)) { if (evt.getOldValue() instanceof Caret ) { 662 ((Caret )evt.getOldValue()).removeChangeListener(this); 663 } 664 if (evt.getNewValue() instanceof Caret ) { 665 ((Caret )evt.getNewValue()).addChangeListener(this); 666 } 667 668 } else if ("enabled".equals(propName)) { if (!component.isEnabled()) { 670 component.getCaret().setVisible(false); 671 } 672 } 673 } 674 675 protected Map createColoringMap() { 676 Map cm; 677 678 if (!usePrintColoringMap) { 679 cm = getSharedColoringMap(getKitClass()); 681 682 } else { cm = SettingsUtil.getColoringMap(getKitClass(), true, true); 684 if (cm.get(SettingsNames.DEFAULT_COLORING) == null) { 686 cm.put(SettingsNames.DEFAULT_COLORING, SettingsDefaults.defaultColoring); 687 } 688 } 689 690 return cm; 691 } 692 693 public int getLineHeight() { 694 return lineHeight; 695 } 696 697 public int getLineAscent() { 698 return lineAscent; 699 } 700 701 public Map getColoringMap() { 702 synchronized (Settings.class) { 704 if (coloringMap == null) { 705 coloringMap = createColoringMap(); 706 } 707 return coloringMap; 708 } 709 } 710 711 public Coloring getDefaultColoring() { 712 return (Coloring)getColoringMap().get(SettingsNames.DEFAULT_COLORING); 713 } 714 715 public Coloring getColoring(String coloringName) { 716 return (Coloring)getColoringMap().get(coloringName); 717 } 718 719 private void updateLineHeight(JTextComponent component) { 720 if (component == null) { 721 return; 722 } 723 if (debugUpdateLineHeight) { 724 System.err.println("EditorUI.updateLineHeight(): Computing lineHeight ..."); 725 } 726 Map cm = getColoringMap(); 727 Iterator i = cm.entrySet().iterator(); 728 int maxHeight = 1; 729 int maxAscent = 0; 730 while (i.hasNext()) { 731 Map.Entry me = (Map.Entry )i.next(); 732 String coloringName = (String )me.getKey(); 733 if (SettingsNames.STATUS_BAR_COLORING.equals(coloringName) 734 || SettingsNames.STATUS_BAR_BOLD_COLORING.equals(coloringName)) { 735 continue; 737 } 738 Coloring c = (Coloring)me.getValue(); 739 if (c != null) { 740 Font font = c.getFont(); 741 if (font != null && (c.getFontMode() & Coloring.FONT_MODE_APPLY_SIZE) != 0) { 742 FontMetrics fm = FontMetricsCache.getFontMetrics(font, component); 743 if (fm != null) { 744 if (debugUpdateLineHeight) { 745 if (maxHeight < fm.getHeight()) { 746 System.err.println("Updating maxHeight from " 747 + maxHeight + " to " + fm.getHeight() + ", coloringName=" + coloringName + ", font=" + font ); 751 } 752 753 if (maxHeight < fm.getHeight()) { 754 System.err.println("Updating maxAscent from " 755 + maxAscent + " to " + fm.getAscent() + ", coloringName=" + coloringName + ", font=" + font ); 759 } 760 } 761 762 maxHeight = Math.max(maxHeight, fm.getHeight()); 763 maxAscent = Math.max(maxAscent, fm.getAscent()); 764 } 765 } 766 } 767 } 768 769 boolean changePreferences = false; 770 if (lineHeight!=1 && lineHeight!=(int)(maxHeight * lineHeightCorrection)){ 771 changePreferences = true; 772 } 773 774 int oldProp = lineHeight; 775 776 lineHeight = (int)(maxHeight * lineHeightCorrection); 778 lineAscent = (int)(maxAscent * lineHeightCorrection); 779 if (changePreferences) { 780 firePropertyChange(LINE_HEIGHT_CHANGED_PROP, new Integer (oldProp), new Integer (lineHeight)); 781 } 782 783 } 784 785 788 void updateComponentProperties() { 789 Class kitClass = Utilities.getKitClass(component); 790 791 if (kitClass != null) { 793 Object value = Settings.getValue(kitClass, SettingsNames.MARGIN); 794 Insets margin = (value instanceof Insets) ? (Insets)value : null; 795 component.setMargin(margin); 796 } 797 798 getDefaultColoring().apply(component); 800 801 lineNumberDigitWidth = computeLineNumberDigitWidth(); 802 803 updateLineHeight(getComponent()); 805 806 FontMetricsCache.Info fmcInfo = FontMetricsCache.getInfo(getDefaultColoring().getFont()); 808 defaultSpaceWidth = fmcInfo.getSpaceWidth(component); 809 810 updateLineNumberWidth(0); 811 812 if (isGlyphGutterVisible()) { 814 glyphGutter.update(); 815 updateScrollPaneCornerColor(); 816 } 817 } 818 819 protected void update(Graphics g) { 820 if (renderingHints != null) { 822 ((Graphics2D)g).setRenderingHints(renderingHints); 823 } 824 } 825 826 public final JTextComponent getComponent() { 827 return component; 828 } 829 830 834 public final BaseDocument getDocument() { 835 return (component != null) ? Utilities.getDocument(component) : printDoc; 836 } 837 838 private Class getKitClass() { 839 return (component != null) ? Utilities.getKitClass(component) 840 : ((printDoc != null) ? printDoc.getKitClass() : null); 841 } 842 843 public Object getProperty(Object key) { 844 return props.get(key); 845 } 846 847 public void putProperty(Object key, Object value) { 848 Object oldValue; 849 if (value != null) { 850 oldValue = props.put(key, value); 851 } else { 852 oldValue = props.remove(key); 853 } 854 firePropertyChange(key.toString(), oldValue, value); 855 } 856 857 870 public JComponent getExtComponent() { 871 if (extComponent == null) { 872 if (component != null) { 873 extComponent = createExtComponent(); 874 } 875 } 876 return extComponent; 877 } 878 879 882 public JToolBar getToolBarComponent() { 883 if (toolBarComponent == null) { 884 if (component != null) { 885 toolBarComponent = createToolBarComponent(); 886 } 887 } 888 return toolBarComponent; 889 } 890 891 897 protected JToolBar createToolBarComponent() { 898 return null; 899 } 900 901 protected void initGlyphCorner(JScrollPane scroller){ 902 glyphCorner = new JPanel (); 903 updateScrollPaneCornerColor(); 904 scroller.setCorner(JScrollPane.LOWER_LEFT_CORNER, glyphCorner); 905 } 906 907 protected void setGlyphGutter(GlyphGutter gutter){ 908 glyphGutter = gutter; 909 } 910 911 public final int getSideBarWidth(){ 912 JScrollPane scroll = (JScrollPane )SwingUtilities.getAncestorOfClass(JScrollPane .class, getParentViewport()); 913 if (scroll!=null && scroll.getRowHeader()!=null){ 914 Rectangle bounds = scroll.getRowHeader().getBounds(); 915 if (bounds!=null){ 916 return bounds.width; 917 } 918 } 919 return 40; 920 } 921 922 protected JComponent createExtComponent() { 923 setLineNumberEnabled(true); 925 JComponent ec = new JPanel (new BorderLayout()); 927 ec.putClientProperty(JTextComponent .class, component); 928 929 JScrollPane scroller = new JScrollPane (component); 931 scroller.getViewport().setMinimumSize(new Dimension(4,4)); 932 933 scroller.setBorder(null); 935 936 setGlyphGutter(new GlyphGutter(this)); 937 scroller.setRowHeaderView(glyphGutter); 938 939 initGlyphCorner(scroller); 940 941 ec.add(scroller); 942 943 ec.add(getStatusBar().getPanel(), BorderLayout.SOUTH); 945 946 return ec; 947 } 948 949 952 public boolean hasExtComponent() { 953 return (extComponent != null); 954 } 955 956 public Abbrev getAbbrev() { 957 if (abbrev == null) { 958 abbrev = new Abbrev(this, true, true); 959 } 960 return abbrev; 961 } 962 963 public WordMatch getWordMatch() { 964 if (wordMatch == null) { 965 wordMatch = new WordMatch(this); 966 } 967 return wordMatch; 968 } 969 970 public StatusBar getStatusBar() { 971 if (statusBar == null) { 972 statusBar = new StatusBar(this); 973 } 974 return statusBar; 975 } 976 977 978 final DrawLayerList getDrawLayerList() { 979 return drawLayerList; 980 } 981 982 990 public DrawLayer findLayer(String layerName) { 991 return drawLayerList.findLayer(layerName); 992 } 993 994 1006 public boolean addLayer(DrawLayer layer, int visibility) { 1007 return drawLayerList.add(layer, visibility); 1008 } 1009 1010 1016 public DrawLayer removeLayer(String layerName) { 1017 return drawLayerList.remove(layerName); 1018 } 1019 1020 public void repaint(int startY) { 1021 repaint(startY, component.getHeight()); 1022 } 1023 1024 public void repaint(int startY, int height) { 1025 if (height <= 0) { 1026 return; 1027 } 1028 int width = Math.max(component.getWidth(), 0); 1029 startY = Math.max(startY, 0); 1030 component.repaint(0, startY, width, height); 1031 } 1032 1033 public void repaintOffset(int pos) throws BadLocationException { 1034 repaintBlock(pos, pos); 1035 } 1036 1037 1038 public void repaintBlock(int startPos, int endPos) 1039 throws BadLocationException { 1040 BaseTextUI ui = (BaseTextUI)component.getUI(); 1041 if (startPos > endPos) { int tmpPos = startPos; 1043 startPos = endPos; 1044 endPos = tmpPos; 1045 } 1046 1047 int yFrom; 1048 int yTo; 1049 1050 try { 1051 yFrom = ui.getYFromPos(startPos); 1052 } catch (BadLocationException e) { 1053 Utilities.annotateLoggable(e); 1054 yFrom = 0; 1055 } 1056 1057 try { 1058 yTo = ui.getYFromPos(endPos) + lineHeight; 1059 } catch (BadLocationException e) { 1060 Utilities.annotateLoggable(e); 1061 yTo = (int) ui.getRootView(component).getPreferredSpan(View.Y_AXIS); 1062 } 1063 1064 repaint(yFrom, yTo - yFrom); 1065 } 1066 1067 1068 private JViewport getParentViewport() { 1069 Component pc = component.getParent(); 1070 return (pc instanceof JViewport ) ? (JViewport )pc : null; 1071 } 1072 1073 1074 public static Frame getParentFrame(Component c) { 1075 do { 1076 c = c.getParent(); 1077 if (c instanceof Frame) { 1078 return (Frame)c; 1079 } 1080 } while (c != null); 1081 return null; 1082 } 1083 1084 1088 public boolean updateVirtualWidth(int width) { 1089 return false; 1090 } 1091 1092 1100 public boolean updateVirtualHeight(int height) { 1101 return false; 1102 } 1103 1104 public boolean isLineNumberEnabled() { 1105 return lineNumberEnabled; 1106 } 1107 1108 public void setLineNumberEnabled(boolean lineNumberEnabled) { 1109 this.lineNumberEnabled = lineNumberEnabled; 1110 lineNumberVisible = lineNumberEnabled && lineNumberVisibleSetting; 1111 if (disableLineNumbers) 1112 lineNumberVisible = false; 1113 } 1114 1115 void setLineNumberVisibleSetting(boolean lineNumberVisibleSetting) { 1116 this.lineNumberVisibleSetting = lineNumberVisibleSetting; 1117 } 1118 1119 1123 public void updateLineNumberWidth(int maxDigitCount) { 1124 int oldWidth = lineNumberWidth; 1125 1126 if (lineNumberVisible) { 1127 try { 1128 if (maxDigitCount <= 0) { 1129 BaseDocument doc = getDocument(); 1130 int lineCnt = Utilities.getLineOffset(doc, doc.getLength()) + 1; 1131 maxDigitCount = Integer.toString(lineCnt).length(); 1132 } 1133 1134 if (maxDigitCount > lineNumberMaxDigitCount) { 1135 lineNumberMaxDigitCount = maxDigitCount; 1136 } 1137 1138 } catch (BadLocationException e) { 1139 lineNumberMaxDigitCount = 1; 1140 } 1141 lineNumberWidth = lineNumberMaxDigitCount * lineNumberDigitWidth; 1142 Insets lineMargin = getLineNumberMargin(); 1143 if (lineMargin != null) { 1144 lineNumberWidth += lineMargin.left + lineMargin.right; 1145 } 1146 1147 } else { 1148 lineNumberWidth = 0; 1149 } 1150 1151 updateTextMargin(); 1152 if (oldWidth != lineNumberWidth) { if (component != null) { 1154 component.repaint(); 1155 } 1156 } 1157 } 1158 1159 public void updateTextMargin() { 1160 if (!SwingUtilities.isEventDispatchThread()) { 1161 SwingUtilities.invokeLater( 1162 new Runnable () { 1163 public void run() { 1164 updateTextMargin(); 1165 } 1166 } 1167 ); 1168 } 1169 1170 Insets orig = textMargin; 1171 Insets cm = (component != null) ? component.getMargin() : null; 1172 int leftWidth = lineNumberWidth + textLeftMarginWidth; 1173 if (cm != null) { 1174 textMargin = new Insets(cm.top, cm.left + leftWidth, 1175 cm.bottom, cm.right); 1176 } else { 1177 textMargin = new Insets(0, leftWidth, 0, 0); 1178 } 1179 if (orig.top != textMargin.top || orig.bottom != textMargin.bottom) { 1180 ((BaseTextUI)component.getUI()).invalidateStartY(); 1181 } 1182 } 1183 1184 public Rectangle getExtentBounds() { 1185 return getExtentBounds(null); 1186 } 1187 1188 1191 public Rectangle getExtentBounds(Rectangle r) { 1192 if (r == null) { 1193 r = new Rectangle(); 1194 } 1195 if (component != null) { 1196 JViewport port = getParentViewport(); 1197 if (port != null) { 1198 Point p = port.getViewPosition(); 1199 r.width = port.getWidth(); 1200 r.height = port.getHeight(); 1201 r.x = p.x; 1202 r.y = p.y; 1203 } else { r.setBounds(component.getVisibleRect()); 1205 } 1206 } 1207 return r; 1208 } 1209 1210 1211 public Insets getTextMargin() { 1212 return textMargin; 1213 } 1214 1215 1224 public void scrollRectToVisible(final Rectangle r, final int scrollPolicy) { 1225 Utilities.runInEventDispatchThread( 1226 new Runnable () { 1227 public void run() { 1228 scrollRectToVisibleFragile(r, scrollPolicy); 1229 } 1230 } 1231 ); 1232 } 1233 1234 1235 boolean scrollRectToVisibleFragile(Rectangle r, int scrollPolicy) { 1236 Insets margin = getTextMargin(); 1237 Rectangle bounds = getExtentBounds(); 1238 r = new Rectangle(r); r.x -= margin.left; 1240 r.y -= margin.top; 1241 bounds.width -= margin.left + margin.right; 1242 bounds.height -= margin.top + margin.bottom; 1243 return scrollRectToVisibleImpl(r, scrollPolicy, bounds); 1244 } 1245 1246 1250 private boolean scrollRectToVisibleImpl(Rectangle r, int scrollPolicy, 1251 Rectangle bounds) { 1252 if (bounds.width <= 0 || bounds.height <= 0) { 1253 return false; 1254 } 1255 1256 if (scrollPolicy == SCROLL_FIND) { 1258 int cnvFI = (scrollFindInsets.left < 0) 1260 ? (- bounds.width * scrollFindInsets.left / 100) 1261 : scrollFindInsets.left * defaultSpaceWidth; 1262 1263 int nx = Math.max(r.x - cnvFI, 0); 1264 1265 cnvFI = (scrollFindInsets.right < 0) 1266 ? (- bounds.width * scrollFindInsets.right / 100) 1267 : scrollFindInsets.right * defaultSpaceWidth; 1268 1269 r.width += (r.x - nx) + cnvFI; 1270 r.x = nx; 1271 1272 cnvFI = (scrollFindInsets.top < 0) 1273 ? (- bounds.height * scrollFindInsets.top / 100) 1274 : scrollFindInsets.top * lineHeight; 1275 1276 int ny = Math.max(r.y - cnvFI, 0); 1277 1278 cnvFI = (scrollFindInsets.bottom < 0) 1279 ? (- bounds.height * scrollFindInsets.bottom / 100) 1280 : scrollFindInsets.bottom * lineHeight; 1281 1282 r.height += (r.y - ny) + cnvFI; 1283 r.y = ny; 1284 1285 return scrollRectToVisibleImpl(r, SCROLL_SMALLEST, bounds); } 1287 1288 int viewWidth = (int)((TextUI )component.getUI()).getRootView(component).getPreferredSpan(View.X_AXIS); 1289 int viewHeight = (int)((TextUI )component.getUI()).getRootView(component).getPreferredSpan(View.Y_AXIS); 1290 1291 if (r.x + r.width > viewWidth) { 1293 r.x = viewWidth - r.width; 1294 if (r.x < 0) { 1295 r.x = 0; 1296 r.width = viewWidth; 1297 } 1298 return scrollRectToVisibleImpl(r, scrollPolicy, bounds); } 1300 if (r.y +r.height > viewHeight) { 1302 r.y = viewHeight - r.height; 1303 if (r.y < 0) { 1304 r.y = 0; 1305 r.height = viewHeight; 1306 } 1307 return scrollRectToVisibleImpl(r, scrollPolicy, bounds); 1308 } 1309 1310 if (r.width > bounds.width || r.height > bounds.height) { 1312 try { 1313 Rectangle caretRect = component.getUI().modelToView( 1314 component, component.getCaret().getDot(), Position.Bias.Forward); 1315 if (caretRect.x >= r.x 1316 && caretRect.x + caretRect.width <= r.x + r.width 1317 && caretRect.y >= r.y 1318 && caretRect.y + caretRect.height <= r.y + r.height 1319 ) { int overX = r.width - bounds.width; 1322 int overY = r.height - bounds.height; 1323 if (overX > 0) { 1324 r.x -= overX * (caretRect.x - r.x) / r.width; 1325 } 1326 if (overY > 0) { 1327 r.y -= overY * (caretRect.y - r.y) / r.height; 1328 } 1329 } 1330 r.height = bounds.height; 1331 r.width = bounds.width; return scrollRectToVisibleImpl(r, scrollPolicy, bounds); 1333 } catch (BadLocationException ble){ 1334 ble.printStackTrace(); 1335 } 1336 } 1337 1338 int newX = bounds.x; 1339 int newY = bounds.y; 1340 boolean move = false; 1341 if (r.x < bounds.x) { 1344 move = true; 1345 switch (scrollPolicy) { 1346 case SCROLL_MOVE: 1347 newX = (scrollJumpInsets.left < 0) 1348 ? (bounds.width * (-scrollJumpInsets.left) / 100) 1349 : scrollJumpInsets.left * defaultSpaceWidth; 1350 newX = Math.min(newX, bounds.x + bounds.width - (r.x + r.width)); 1351 newX = Math.max(r.x - newX, 0); break; 1353 case SCROLL_DEFAULT: 1354 case SCROLL_SMALLEST: 1355 default: 1356 newX = r.x; 1357 break; 1358 } 1359 updateVirtualWidth(newX + bounds.width); 1360 } else if (r.x + r.width > bounds.x + bounds.width) { 1361 move = true; 1362 switch (scrollPolicy) { 1363 case SCROLL_SMALLEST: 1364 newX = r.x + r.width - bounds.width; 1365 break; 1366 default: 1367 newX = (scrollJumpInsets.right < 0) 1368 ? (bounds.width * (-scrollJumpInsets.right) / 100 ) 1369 : scrollJumpInsets.right * defaultSpaceWidth; 1370 newX = Math.min(newX, bounds.width - r.width); 1371 newX = (r.x + r.width) + newX - bounds.width; 1372 break; 1373 } 1374 1375 updateVirtualWidth(newX + bounds.width); 1376 } 1377 1378 if (r.y < bounds.y) { 1379 move = true; 1380 switch (scrollPolicy) { 1381 case SCROLL_MOVE: 1382 newY = r.y; 1383 newY -= (scrollJumpInsets.top < 0) 1384 ? (bounds.height * (-scrollJumpInsets.top) / 100 ) 1385 : scrollJumpInsets.top * lineHeight; 1386 break; 1387 case SCROLL_SMALLEST: 1388 newY = r.y; 1389 break; 1390 case SCROLL_DEFAULT: 1391 default: 1392 newY = r.y - (bounds.height - r.height) / 2; break; 1394 } 1395 newY = Math.max(newY, 0); 1396 } else if (r.y + r.height > bounds.y + bounds.height) { 1397 move = true; 1398 switch (scrollPolicy) { 1399 case SCROLL_MOVE: 1400 newY = (r.y + r.height) - bounds.height; 1401 newY += (scrollJumpInsets.bottom < 0) 1402 ? (bounds.height * (-scrollJumpInsets.bottom) / 100 ) 1403 : scrollJumpInsets.bottom * lineHeight; 1404 break; 1405 case SCROLL_SMALLEST: 1406 newY = (r.y + r.height) - bounds.height; 1407 break; 1408 case SCROLL_DEFAULT: 1409 default: 1410 newY = r.y - (bounds.height - r.height) / 2; break; 1412 } 1413 newY = Math.max(newY, 0); 1414 } 1415 1416 if (move) { 1417 setExtentPosition(newX, newY); 1418 } 1419 return move; 1420 } 1421 1422 void setExtentPosition(int x, int y) { 1423 JViewport port = getParentViewport(); 1424 if (port != null) { 1425 Point p = new Point(Math.max(x, 0), Math.max(y, 0)); 1426 port.setViewPosition(p); 1427 } 1428 } 1429 1430 public void adjustWindow(int caretPercentFromWindowTop) { 1431 final Rectangle bounds = getExtentBounds(); 1432 if (component != null && (component.getCaret() instanceof Rectangle)) { 1433 Rectangle caretRect = (Rectangle)component.getCaret(); 1434 bounds.y = caretRect.y - (caretPercentFromWindowTop * bounds.height) / 100 1435 + (caretPercentFromWindowTop * lineHeight) / 100; 1436 Utilities.runInEventDispatchThread( 1437 new Runnable () { 1438 public void run() { 1439 scrollRectToVisible(bounds, SCROLL_SMALLEST); 1440 } 1441 } 1442 ); 1443 } 1444 } 1445 1446 1450 public void adjustCaret(int percentFromWindowTop) { 1451 JTextComponent c = component; 1452 if (c != null) { 1453 Rectangle bounds = getExtentBounds(); 1454 bounds.y += (percentFromWindowTop * bounds.height) / 100 1455 - (percentFromWindowTop * lineHeight) / 100; 1456 try { 1457 int offset = ((BaseTextUI)c.getUI()).getPosFromY(bounds.y); 1458 if (offset >= 0) { 1459 caretSetDot(offset, null, SCROLL_SMALLEST); 1460 } 1461 } catch (BadLocationException e) { 1462 } 1463 } 1464 } 1465 1466 1473 public void caretSetDot(int offset, Rectangle scrollRect, int scrollPolicy) { 1474 if (component != null) { 1475 Caret caret = component.getCaret(); 1476 if (caret instanceof BaseCaret) { 1477 ((BaseCaret)caret).setDot(offset, scrollRect, scrollPolicy); 1478 } else { 1479 caret.setDot(offset); 1480 } 1481 } 1482 } 1483 1484 1491 public void caretMoveDot(int offset, Rectangle scrollRect, int scrollPolicy) { 1492 if (component != null) { 1493 Caret caret = component.getCaret(); 1494 if (caret instanceof BaseCaret) { 1495 ((BaseCaret)caret).moveDot(offset, scrollRect, scrollPolicy); 1496 } else { 1497 caret.moveDot(offset); 1498 } 1499 } 1500 } 1501 1502 1508 protected void paint(Graphics g) { 1509 if (component != null) { update(g); 1511 } 1512 } 1513 1514 1515 public Insets getLineNumberMargin() { 1516 return defaultLineNumberMargin; 1517 } 1518 1519 private int computeLineNumberDigitWidth(){ 1520 Coloring dc = getDefaultColoring(); 1522 Coloring lnc = (Coloring)getColoringMap().get(SettingsNames.LINE_NUMBER_COLORING); 1523 if (lnc != null) { 1524 Font lnFont = lnc.getFont(); 1525 if (lnFont == null) { 1526 lnFont = dc.getFont(); 1527 } 1528 if (component == null) return lineNumberDigitWidth; 1529 FontMetrics lnFM = FontMetricsCache.getFontMetrics(lnFont, component); 1530 if (lnFM == null) return lineNumberDigitWidth; 1531 int maxWidth = 1; 1532 char[] digit = new char[1]; for (int i = 0; i <= 9; i++) { 1534 digit[0] = (char)('0' + i); 1535 maxWidth = Math.max(maxWidth, lnFM.charsWidth(digit, 0, 1)); 1536 } 1537 return maxWidth; 1538 } 1539 return lineNumberDigitWidth; 1540 } 1541 1542 1543 public int getLineNumberDigitWidth() { 1544 return lineNumberDigitWidth; 1545 } 1546 1547 1548 public boolean isGlyphGutterVisible() { 1549 return glyphGutter != null; 1550 } 1551 1552 public final GlyphGutter getGlyphGutter() { 1553 return glyphGutter; 1554 } 1555 1556 protected void updateScrollPaneCornerColor() { 1557 Coloring lineColoring = (Coloring)getColoringMap().get(SettingsNames.LINE_NUMBER_COLORING); 1558 Coloring defaultColoring = (Coloring)getDefaultColoring(); 1559 1560 Color backgroundColor; 1561 if (lineColoring.getBackColor() != null) 1562 backgroundColor = lineColoring.getBackColor(); 1563 else 1564 backgroundColor = defaultColoring.getBackColor(); 1565 1566 if (glyphCorner != null){ 1567 glyphCorner.setBackground(backgroundColor); 1568 } 1569 } 1570} 1571 | Popular Tags |