1 19 20 package org.netbeans.modules.editor.lib; 21 22 import java.lang.ref.WeakReference ; 23 import java.util.ArrayList ; 24 import java.util.Collections ; 25 import java.util.List ; 26 import java.util.logging.Level ; 27 import java.util.logging.Logger ; 28 import javax.swing.JEditorPane ; 29 import javax.swing.text.AttributeSet ; 30 import javax.swing.text.Document ; 31 import javax.swing.text.EditorKit ; 32 import javax.swing.text.JTextComponent ; 33 import javax.swing.text.SimpleAttributeSet ; 34 import org.netbeans.api.editor.mimelookup.MimeLookup; 35 import org.netbeans.api.editor.mimelookup.MimePath; 36 import org.netbeans.api.editor.settings.AttributesUtilities; 37 import org.netbeans.editor.BaseDocument; 38 import org.netbeans.editor.Coloring; 39 import org.netbeans.editor.DrawContext; 40 import org.netbeans.editor.DrawLayer; 41 import org.netbeans.editor.EditorUI; 42 import org.netbeans.editor.MarkFactory; 43 import org.netbeans.editor.ext.ExtCaret; 44 import org.netbeans.modules.editor.lib2.highlighting.CaretBasedBlockHighlighting.CaretRowHighlighting; 45 import org.netbeans.modules.editor.lib2.highlighting.HighlightingManager; 46 import org.netbeans.modules.editor.lib2.highlighting.HighlightingSpiPackageAccessor; 47 import org.netbeans.modules.editor.lib2.highlighting.HighlightsLayerAccessor; 48 import org.netbeans.modules.editor.lib2.highlighting.HighlightsLayerFilter; 49 import org.netbeans.spi.editor.highlighting.HighlightsChangeEvent; 50 import org.netbeans.spi.editor.highlighting.HighlightsChangeListener; 51 import org.netbeans.spi.editor.highlighting.HighlightsContainer; 52 import org.netbeans.spi.editor.highlighting.HighlightsLayer; 53 import org.netbeans.spi.editor.highlighting.HighlightsSequence; 54 55 59 public class HighlightingDrawLayer extends DrawLayer.AbstractLayer 60 implements HighlightsChangeListener 61 { 62 private static final Logger LOG = Logger.getLogger(HighlightingDrawLayer.class.getName()); 63 64 private static final String LAYER_A_NAME = "org-netbeans-lib-editor-nview-HighlightingDrawLayer/A"; private static final String LAYER_B_NAME = ExtCaret.HIGHLIGHT_ROW_LAYER_NAME; 67 private static final String LAYER_C_NAME = "org-netbeans-lib-editor-nview-HighlightingDrawLayer/C"; 69 private static final HighlightsLayerFilter FILTER_A = new HighlightsLayerFilter() { 70 public List <? extends HighlightsLayer> filterLayers(List <? extends HighlightsLayer> layers) { 71 ArrayList <HighlightsLayer> filteredLayers = new ArrayList <HighlightsLayer>(); 72 boolean add = false; 73 74 for(HighlightsLayer layer : layers) { 75 HighlightsLayerAccessor layerAccessor = 76 HighlightingSpiPackageAccessor.get().getHighlightsLayerAccessor(layer); 77 78 if (CaretRowHighlighting.LAYER_TYPE_ID.equals(layerAccessor.getLayerTypeId())) { 79 add = true; 80 continue; 81 } 82 83 if (add) { 84 filteredLayers.add(layer); 85 } 86 } 87 88 return filteredLayers; 89 } 90 }; 92 private static final HighlightsLayerFilter FILTER_B = new HighlightsLayerFilter() { 93 public List <? extends HighlightsLayer> filterLayers(List <? extends HighlightsLayer> layers) { 94 ArrayList <HighlightsLayer> filteredLayers = new ArrayList <HighlightsLayer>(); 95 96 for(HighlightsLayer layer : layers) { 97 HighlightsLayerAccessor layerAccessor = 98 HighlightingSpiPackageAccessor.get().getHighlightsLayerAccessor(layer); 99 100 if (CaretRowHighlighting.LAYER_TYPE_ID.equals(layerAccessor.getLayerTypeId())) { 101 filteredLayers.add(layer); 102 break; 103 } 104 } 105 106 return filteredLayers; 107 } 108 }; 110 private static final HighlightsLayerFilter FILTER_C = new HighlightsLayerFilter() { 111 public List <? extends HighlightsLayer> filterLayers(List <? extends HighlightsLayer> layers) { 112 ArrayList <HighlightsLayer> filteredLayers = new ArrayList <HighlightsLayer>(); 113 114 for(HighlightsLayer layer : layers) { 115 HighlightsLayerAccessor layerAccessor = 116 HighlightingSpiPackageAccessor.get().getHighlightsLayerAccessor(layer); 117 118 if (CaretRowHighlighting.LAYER_TYPE_ID.equals(layerAccessor.getLayerTypeId())) { 119 break; 120 } 121 122 filteredLayers.add(layer); 123 } 124 125 return filteredLayers; 126 } 127 }; 129 public static void hookUp(EditorUI eui) { 130 DrawLayer layerA = eui.findLayer(LAYER_A_NAME); 131 if (layerA == null) { 132 layerA = new HighlightingDrawLayer(LAYER_A_NAME, FILTER_A); 133 eui.addLayer(layerA, 10000); 135 if (LOG.isLoggable(Level.FINE)) { 136 LOG.fine("Successfully registered layerA in " + simpleToString(eui)); } 138 } else { 139 if (LOG.isLoggable(Level.FINE)) { 140 LOG.fine("LayerA is already registered in " + simpleToString(eui)); } 142 } 143 144 DrawLayer layerB = eui.findLayer(LAYER_B_NAME); 145 if (layerB == null) { 146 layerB = new HighlightingDrawLayer(LAYER_B_NAME, FILTER_B); 147 eui.addLayer(layerB, 2050); 149 if (LOG.isLoggable(Level.FINE)) { 150 LOG.fine("Successfully registered layerB in " + simpleToString(eui)); } 152 } else { 153 if (LOG.isLoggable(Level.FINE)) { 154 LOG.fine("LayerB is already registered in " + simpleToString(eui)); } 156 } 157 158 DrawLayer layerC = eui.findLayer(LAYER_C_NAME); 159 if (layerC == null) { 160 layerC = new HighlightingDrawLayer(LAYER_C_NAME, FILTER_C); 161 eui.addLayer(layerC, 1000); 163 if (LOG.isLoggable(Level.FINE)) { 164 LOG.fine("Successfully registered layerC in " + simpleToString(eui)); } 166 } else { 167 if (LOG.isLoggable(Level.FINE)) { 168 LOG.fine("LayerC is already registered in " + simpleToString(eui)); } 170 } 171 } 172 173 private final HighlightsLayerFilter filter; 174 175 private WeakReference <JTextComponent > paneRef = null; 176 private HighlightsContainer highlights = null; 177 178 private AttributeSet lastAttributeSet = null; 179 180 private AttributeSet lastEOLAttribs = null; 182 private AttributeSet lastELAttribs = null; 183 private boolean theLittleSpitAtTheBeginningOfAnEmptyLineDrawn = false; 184 185 private HighlightingDrawLayer(String name, HighlightsLayerFilter filter) { 186 super(name); 187 this.filter = filter; 188 } 189 190 public void init(DrawContext ctx) { 191 super.init(ctx); 192 193 if (highlights == null) { 194 JTextComponent pane = ctx.getEditorUI().getComponent(); 196 197 if (pane == null) { 200 Document doc = ctx.getEditorUI().getDocument(); 201 202 String mimeType = (String ) doc.getProperty("mimeType"); assert mimeType != null : "Document's mime type can't be null: " + doc; 206 216 JEditorPane fakePane = new JEditorPane (); 218 fakePane.setDocument(doc); 220 221 fakePane.putClientProperty("HighlightsLayerIncludes", new String [] { "^.*NonLexerSyntaxHighlighting$", "^.*SyntaxHighlighting$"}); 224 pane = fakePane; 225 } 226 this.paneRef = new WeakReference <JTextComponent >(pane); 227 228 HighlightingManager hm = HighlightingManager.getInstance(); 229 this.highlights = hm.getHighlights(pane, filter); 230 this.highlights.addHighlightsChangeListener(this); 231 } 232 233 lastAttributeSet = null; 234 235 lastEOLAttribs = null; 237 lastELAttribs = null; 238 theLittleSpitAtTheBeginningOfAnEmptyLineDrawn = false; 239 } 240 241 public boolean isActive(DrawContext ctx, MarkFactory.DrawMark mark) { 242 if (highlights != null) { 243 return processOffset(ctx, false); 244 } else { 245 return false; 246 } 247 } 248 249 public void updateContext(DrawContext ctx) { 250 if (highlights != null) { 251 if (ctx.isEOL() && ctx.isBOL()) { 252 if (extendsEmptyLine() && !theLittleSpitAtTheBeginningOfAnEmptyLineDrawn) { 253 theLittleSpitAtTheBeginningOfAnEmptyLineDrawn = true; 254 Coloring coloring = Coloring.fromAttributeSet(lastELAttribs); 255 coloring.apply(ctx); 256 } else { 257 if (extendsEOL()) { 258 Coloring coloring = Coloring.fromAttributeSet(lastEOLAttribs); 259 coloring.apply(ctx); 260 } 261 } 262 } else if (ctx.isEOL()) { 263 if (extendsEOL()) { 264 Coloring coloring = Coloring.fromAttributeSet(lastEOLAttribs); 265 coloring.apply(ctx); 266 } 267 } else { 268 processOffset(ctx, true); 269 } 270 } 271 } 272 273 public boolean extendsEOL() { 274 if (lastEOLAttribs == null && lastAttributeSet != null) { 275 @SuppressWarnings ("unchecked") 276 List <AttributeSet > allSets = (List <AttributeSet >) lastAttributeSet.getAttribute("dismantled-structure"); AttributeSet [] arr = filter(allSets != null ? allSets : Collections.singletonList(lastAttributeSet)); 278 lastEOLAttribs = arr[0]; 279 lastELAttribs = arr[1]; 280 } 281 282 return lastEOLAttribs != null && lastEOLAttribs != SimpleAttributeSet.EMPTY; 283 } 284 285 public boolean extendsEmptyLine() { 286 if (lastELAttribs == null && lastAttributeSet != null) { 287 @SuppressWarnings ("unchecked") 288 List <AttributeSet > allSets = (List <AttributeSet >) lastAttributeSet.getAttribute("dismantled-structure"); AttributeSet [] arr = filter(allSets != null ? allSets : Collections.singletonList(lastAttributeSet)); 290 lastEOLAttribs = arr[0]; 291 lastELAttribs = arr[1]; 292 } 293 294 return lastELAttribs != null && lastELAttribs != SimpleAttributeSet.EMPTY; 295 } 296 297 301 public void highlightChanged(HighlightsChangeEvent event) { 302 if (LOG.isLoggable(Level.FINEST)) { 303 LOG.finest("BRIDGE-LAYER: changed area [" + event.getStartOffset() + ", " + event.getEndOffset() + "]"); 305 } 334 setNextActivityChangeOffset(0); 335 336 JTextComponent pane = paneRef.get(); 337 if (pane != null) { 338 int rangeEnd = Math.min(event.getEndOffset(), pane.getDocument().getLength()); 339 int rangeStart = event.getStartOffset() >= rangeEnd ? 0 : event.getStartOffset(); 340 341 if (rangeStart < rangeEnd) { 342 try { 343 pane.getUI().damageRange(pane, rangeStart, rangeEnd); 344 } catch (Exception e) { 345 LOG.log(Level.INFO, "Can't update view: range = [" + rangeStart + ", " + rangeEnd + "]", e); } 347 } 348 } 349 } 350 351 355 private boolean processOffset(DrawContext ctx, boolean applyAttributes) { 356 BaseDocument doc = ctx.getEditorUI().getDocument(); 357 int currentOffset = ctx.getFragmentOffset(); 358 int endOffset = doc.getParagraphElement(currentOffset).getEndOffset(); 359 360 if (endOffset >= doc.getLength()) { 361 endOffset = Integer.MAX_VALUE; 362 } 363 364 HighlightsSequence hs = highlights.getHighlights(currentOffset, endOffset); 365 boolean hasHighlight = hs.moveNext(); 366 367 if (hasHighlight) { 368 if (hs.getStartOffset() <= currentOffset) { 369 if (applyAttributes) { 370 Coloring coloring = Coloring.fromAttributeSet(hs.getAttributes()); 371 coloring.apply(ctx); 372 } 373 374 lastAttributeSet = hs.getAttributes(); 375 setNextActivityChangeOffset(hs.getEndOffset()); 376 } else { 377 setNextActivityChangeOffset(hs.getStartOffset()); 378 } 379 380 return true; 381 } else { 382 return false; 383 } 384 } 385 386 private AttributeSet [] filter(List <AttributeSet > sets) { 387 ArrayList <AttributeSet > eolSets = new ArrayList <AttributeSet >(); 388 ArrayList <AttributeSet > elSets = new ArrayList <AttributeSet >(); 389 390 for(AttributeSet set : sets) { 391 Object value = set.getAttribute(HighlightsContainer.ATTR_EXTENDS_EOL); 392 393 if ((value instanceof Boolean ) && ((Boolean ) value).booleanValue()) { 394 eolSets.add(set); 395 } 396 397 value = set.getAttribute(HighlightsContainer.ATTR_EXTENDS_EMPTY_LINE); 398 if ((value instanceof Boolean ) && ((Boolean ) value).booleanValue()) { 399 elSets.add(set); 400 } 401 } 402 403 AttributeSet eolAttribs; 404 if (eolSets.size() > 1) { 405 eolAttribs = AttributesUtilities.createComposite(eolSets.toArray(new AttributeSet [eolSets.size()])); 406 } else if (eolSets.size() == 1) { 407 eolAttribs = eolSets.get(0); 408 } else { 409 eolAttribs = SimpleAttributeSet.EMPTY; 410 } 411 412 AttributeSet elAttribs; 413 if (elSets.size() > 1) { 414 elAttribs = AttributesUtilities.createComposite(elSets.toArray(new AttributeSet [elSets.size()])); 415 } else if (elSets.size() == 1) { 416 elAttribs = elSets.get(0); 417 } else { 418 elAttribs = SimpleAttributeSet.EMPTY; 419 } 420 421 return new AttributeSet [] { eolAttribs, elAttribs }; 422 } 423 424 private static String simpleToString(Object o) { 425 return o == null ? "null" : o.getClass() + "@" + Integer.toHexString(System.identityHashCode(o)); } 427 } 428 | Popular Tags |