1 19 20 package org.netbeans.modules.editor.lib2.highlighting; 21 22 import java.beans.PropertyChangeEvent ; 23 import java.beans.PropertyChangeListener ; 24 import java.util.logging.Level ; 25 import java.util.logging.Logger ; 26 import javax.swing.event.ChangeEvent ; 27 import javax.swing.event.ChangeListener ; 28 import javax.swing.text.AttributeSet ; 29 import javax.swing.text.BadLocationException ; 30 import javax.swing.text.Caret ; 31 import javax.swing.text.Document ; 32 import javax.swing.text.EditorKit ; 33 import javax.swing.text.JTextComponent ; 34 import javax.swing.text.Position ; 35 import javax.swing.text.SimpleAttributeSet ; 36 import org.netbeans.api.editor.mimelookup.MimeLookup; 37 import org.netbeans.api.editor.mimelookup.MimePath; 38 import org.netbeans.api.editor.settings.AttributesUtilities; 39 import org.netbeans.api.editor.settings.FontColorNames; 40 import org.netbeans.api.editor.settings.FontColorSettings; 41 import org.netbeans.modules.editor.lib2.DocUtils; 42 import org.netbeans.spi.editor.highlighting.HighlightsSequence; 43 import org.netbeans.spi.editor.highlighting.support.AbstractHighlightsContainer; 44 import org.openide.util.WeakListeners; 45 46 51 public abstract class CaretBasedBlockHighlighting extends AbstractHighlightsContainer implements ChangeListener , PropertyChangeListener { 52 53 private static final Logger LOG = Logger.getLogger(CaretBasedBlockHighlighting.class.getName()); 54 55 private final MimePath mimePath; 56 private final JTextComponent component; 57 private Caret caret; 58 private ChangeListener caretListener; 59 60 private final String coloringName; 61 private final boolean extendsEOL; 62 private final boolean extendsEmptyLine; 63 64 private Position currentLineStart; 65 private Position currentLineEnd; 66 67 68 protected CaretBasedBlockHighlighting(JTextComponent component, String coloringName, boolean extendsEOL, boolean extendsEmptyLine) { 69 EditorKit kit = component.getUI().getEditorKit(component); 71 String mimeType = kit == null ? null : kit.getContentType(); 72 this.mimePath = mimeType == null ? MimePath.EMPTY : MimePath.parse(mimeType); 73 74 this.coloringName = coloringName; 75 this.extendsEOL = extendsEOL; 76 this.extendsEmptyLine = extendsEmptyLine; 77 78 this.component = component; 80 this.component.addPropertyChangeListener(WeakListeners.propertyChange(this, this.component)); 81 82 this.caret = component.getCaret(); 84 if (this.caret != null) { 85 this.caretListener = WeakListeners.change(this, this.caret); 86 this.caret.addChangeListener(caretListener); 87 } 88 89 updateLineInfo(false); 91 } 92 93 97 public final HighlightsSequence getHighlights(int startOffset, int endOffset) { 98 if (currentLineStart != null && currentLineEnd != null && 99 endOffset >= currentLineStart.getOffset() && startOffset <= currentLineEnd.getOffset()) 100 { 101 return new SimpleHighlightsSequence( 102 Math.max(currentLineStart.getOffset(), startOffset), 103 Math.min(currentLineEnd.getOffset(), endOffset), 104 getAttribs() 105 ); 106 } else { 107 return HighlightsSequence.EMPTY; 108 } 109 } 110 111 115 public final void propertyChange(PropertyChangeEvent evt) { 116 if (evt.getPropertyName() == null || "caret".equals(evt.getPropertyName())) { if (caret != null) { 118 caret.removeChangeListener(caretListener); 119 caretListener = null; 120 } 121 122 caret = component.getCaret(); 123 124 if (caret != null) { 125 caretListener = WeakListeners.change(this, caret); 126 caret.addChangeListener(caretListener); 127 } 128 129 updateLineInfo(true); 130 } 131 } 132 133 137 public final void stateChanged(ChangeEvent e) { 138 updateLineInfo(true); 139 } 140 141 protected abstract Position [] getCurrentBlockPositions(Document document, Caret caret); 142 143 147 private final void updateLineInfo(boolean fire) { 148 Position [] currentLine = getCurrentBlockPositions(component.getDocument(), caret); 149 150 if (!comparePositions(currentLine[0], currentLineStart) || 151 !comparePositions(currentLine[1], currentLineEnd)) 152 { 153 Position changeStart = getLowerPosition(currentLine[0], currentLineStart); 154 Position changeEnd = getHigherPosition(currentLine[1], currentLineEnd); 155 156 if (LOG.isLoggable(Level.FINE)) { 157 LOG.fine("Current row changed from [" + positionToString(currentLineStart) + ", " + positionToString(currentLineEnd) + "] to [" + positionToString(currentLine[0]) + ", " + positionToString(currentLine[1]) + "]"); } 161 162 currentLineStart = currentLine[0]; 163 currentLineEnd = currentLine[1]; 164 165 if (fire) { 166 fireHighlightsChange( 167 changeStart == null ? 0 : changeStart.getOffset(), 168 changeEnd == null ? Integer.MAX_VALUE : changeEnd.getOffset() 169 ); 170 } 171 } 172 } 173 174 private AttributeSet getAttribs() { 175 FontColorSettings fcs = MimeLookup.getLookup(mimePath).lookup(FontColorSettings.class); 176 AttributeSet attribs = fcs.getFontColors(coloringName); 177 178 if (attribs == null) { 179 attribs = SimpleAttributeSet.EMPTY; 180 } else if (extendsEOL || extendsEmptyLine) { 181 attribs = AttributesUtilities.createImmutable( 182 attribs, 183 AttributesUtilities.createImmutable( 184 ATTR_EXTENDS_EOL, Boolean.valueOf(extendsEOL), 185 ATTR_EXTENDS_EMPTY_LINE, Boolean.valueOf(extendsEmptyLine)) 186 ); 187 } 188 189 return attribs; 190 } 191 192 private static boolean comparePositions(Position p1, Position p2) { 193 return (p1 == null && p2 == null) || 194 (p1 != null && p2 != null && p1.getOffset() == p2.getOffset()); 195 } 196 197 private static Position getLowerPosition(Position p1, Position p2) { 198 if (p1 != null && p2 != null) { 199 return p1.getOffset() < p2.getOffset() ? p1 : p2; 200 } else if (p1 != null) { 201 return p1; 202 } else if (p2 != null) { 203 return p2; 204 } else { 205 return null; 206 } 207 } 208 209 private static Position getHigherPosition(Position p1, Position p2) { 210 if (p1 != null && p2 != null) { 211 return p1.getOffset() > p2.getOffset() ? p1 : p2; 212 } else if (p1 != null) { 213 return p1; 214 } else if (p2 != null) { 215 return p2; 216 } else { 217 return null; 218 } 219 } 220 221 private static String positionToString(Position p) { 222 return p == null ? "null" : Integer.toString(p.getOffset()); } 224 225 private static final class SimpleHighlightsSequence implements HighlightsSequence { 226 227 private int startOffset; 228 private int endOffset; 229 private AttributeSet attribs; 230 231 private boolean end = false; 232 233 public SimpleHighlightsSequence(int startOffset, int endOffset, AttributeSet attribs) { 234 this.startOffset = startOffset; 235 this.endOffset = endOffset; 236 this.attribs = attribs; 237 } 238 239 public boolean moveNext() { 240 if (!end) { 241 end = true; 242 return true; 243 } else { 244 return false; 245 } 246 } 247 248 public int getStartOffset() { 249 return startOffset; 250 } 251 252 public int getEndOffset() { 253 return endOffset; 254 } 255 256 public AttributeSet getAttributes() { 257 return attribs; 258 } 259 } 261 public static final class CaretRowHighlighting extends CaretBasedBlockHighlighting { 262 263 public static final String LAYER_TYPE_ID = "org.netbeans.modules.editor.lib2.highlighting.CaretRowHighlighting"; 265 public CaretRowHighlighting(JTextComponent component) { 266 super(component, FontColorNames.CARET_ROW_COLORING, true, false); 267 } 268 269 protected Position [] getCurrentBlockPositions(Document document, Caret caret) { 270 if (document != null && caret != null) { 271 int caretOffset = caret.getDot(); 272 try { 273 int startOffset = DocUtils.getRowStart(document, caretOffset, 0); 274 int endOffset = DocUtils.getRowEnd(document, caretOffset); 275 276 if (endOffset < document.getLength()) { 277 endOffset++; } 279 280 return new Position [] { 281 document.createPosition(startOffset), 282 document.createPosition(endOffset), 283 }; 284 } catch (BadLocationException e) { 285 LOG.log(Level.WARNING, e.getMessage(), e); 286 } 287 } 288 289 return new Position [] { null, null }; 290 } 291 } 293 public static final class TextSelectionHighlighting extends CaretBasedBlockHighlighting { 294 295 public static final String LAYER_TYPE_ID = "org.netbeans.modules.editor.lib2.highlighting.TextSelectionHighlighting"; 297 public TextSelectionHighlighting(JTextComponent component) { 298 super(component, FontColorNames.SELECTION_COLORING, false, true); 299 } 300 301 protected Position [] getCurrentBlockPositions(Document document, Caret caret) { 302 if (document != null && caret != null) { 303 int caretOffset = caret.getDot(); 304 int markOffset = caret.getMark(); 305 306 if (caretOffset != markOffset) { 307 try { 308 return new Position [] { 309 document.createPosition(Math.min(caretOffset, markOffset)), 310 document.createPosition(Math.max(caretOffset, markOffset)), 311 }; 312 } catch (BadLocationException e) { 313 LOG.log(Level.WARNING, e.getMessage(), e); 314 } 315 } 316 } 317 318 return new Position [] { null, null }; 319 } 320 } } 322 | Popular Tags |