1 19 20 package org.netbeans.editor; 21 22 import java.awt.Graphics ; 23 import java.awt.Point ; 24 import java.awt.Rectangle ; 25 import java.awt.Shape ; 26 import javax.swing.event.DocumentEvent ; 27 import javax.swing.text.AbstractDocument ; 28 import javax.swing.text.BadLocationException ; 29 import javax.swing.text.Caret ; 30 import javax.swing.text.Document ; 31 import javax.swing.text.Element ; 32 import javax.swing.text.JTextComponent ; 33 import javax.swing.text.Position ; 34 import javax.swing.text.View ; 35 import javax.swing.text.ViewFactory ; 36 import org.netbeans.editor.view.spi.EstimatedSpanView; 37 import org.netbeans.editor.view.spi.LockView; 38 import org.netbeans.editor.view.spi.ViewLayoutState; 39 import org.netbeans.editor.view.spi.ViewUtilities; 40 import org.openide.ErrorManager; 41 42 48 class DrawEngineLineView extends View implements ViewLayoutState, EstimatedSpanView { 49 50 53 private static final int X_MAJOR_AXIS_BIT = 1; 54 55 58 private static final int MAJOR_AXIS_PREFERENCE_CHANGED_BIT = 2; 59 60 63 private static final int MINOR_AXIS_PREFERENCE_CHANGED_BIT = 4; 64 65 68 private static final int VIEW_SIZE_INVALID_BIT = 8; 69 70 75 private static final int UPDATE_LAYOUT_PENDING_BIT = 16; 76 77 private static final int ESTIMATED_SPAN_BIT = 32; 78 79 protected static final int LAST_USED_BIT = ESTIMATED_SPAN_BIT; 80 81 85 private static final int ANY_INVALID 86 = MAJOR_AXIS_PREFERENCE_CHANGED_BIT 87 | MINOR_AXIS_PREFERENCE_CHANGED_BIT 88 | VIEW_SIZE_INVALID_BIT; 89 90 91 private int statusBits; 93 private int viewRawIndex; 95 private double layoutMajorAxisRawOffset; 97 private float layoutMajorAxisPreferredSpan; 100 private float layoutMinorAxisPreferredSpan; 103 104 105 107 108 ViewToModelDG viewToModelDG; 110 111 public DrawEngineLineView(Element elem) { 112 super(elem); 113 } 114 115 private int getBaseX(int orig) { 116 return orig + getEditorUI().getTextMargin().left; 117 } 118 119 private JTextComponent getComponent() { 120 return (JTextComponent )getContainer(); 121 } 122 123 private BaseTextUI getBaseTextUI(){ 124 return (BaseTextUI)getComponent().getUI(); 125 } 126 127 private EditorUI getEditorUI(){ 128 return getBaseTextUI().getEditorUI(); 129 } 130 131 private ModelToViewDG getModelToViewDG() { 132 138 return new ModelToViewDG(); 139 } 140 141 private ViewToModelDG getViewToModelDG() { 142 if (viewToModelDG == null) { 143 viewToModelDG = new ViewToModelDG(); 144 } 145 return viewToModelDG; 146 } 147 148 public boolean isEstimatedSpan() { 149 return isStatusBitsNonZero(ESTIMATED_SPAN_BIT); 150 } 151 152 public void setEstimatedSpan(boolean estimatedSpan) { 153 if (isEstimatedSpan() != estimatedSpan) { if (estimatedSpan) { 155 setStatusBits(ESTIMATED_SPAN_BIT); 156 } else { clearStatusBits(ESTIMATED_SPAN_BIT); 158 159 getParent().preferenceChanged(this, true, true); 160 } 161 } 162 } 163 164 protected boolean isFragment(){ 165 return false; 166 } 167 168 171 private int getEOLffset(){ 172 return super.getEndOffset() - 1; } 174 175 179 private int getAdjustedEOLOffset() { 180 return Math.min(getEndOffset(), getEOLffset()); 181 } 182 183 public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) { 184 preferenceChanged(this, true, false); 185 } 186 187 public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) { 188 preferenceChanged(this, true, false); 189 } 190 191 public float getAlignment(int axis) { 192 return 0f; 193 } 194 195 public void paint(Graphics g, Shape a) { 196 if (!(getDocument() instanceof BaseDocument)) return; setEstimatedSpan(false); 199 Rectangle allocReadOnly = (a instanceof Rectangle ) ? (Rectangle )a : a.getBounds(); 201 int startOffset = getStartOffset(); 202 int endOffset = getAdjustedEOLOffset(); 203 try{ 204 if (isFragment()){ 205 Rectangle oldClipRect = g.getClipBounds(); 206 Rectangle newClip = new Rectangle (oldClipRect); 207 Rectangle startOffsetClip = modelToView(startOffset, a, Position.Bias.Forward).getBounds(); 208 Rectangle endOffsetClip = modelToView(endOffset, a, Position.Bias.Forward).getBounds(); 209 View parent = getParent(); 210 if (parent instanceof FoldMultiLineView && !equals(parent.getView(parent.getViewCount() - 1))) { 211 newClip.width = Math.min(oldClipRect.width, endOffsetClip.x); 212 213 if (newClip.width + newClip.x > endOffsetClip.x) { 214 newClip.width = newClip.width - (newClip.width + newClip.x - endOffsetClip.x); 215 } 216 217 g.setClip(newClip); 218 } 219 220 int shift = startOffsetClip.x - getEditorUI().getTextMargin().left - allocReadOnly.x; 221 g.translate(-shift,0); 222 223 DrawEngine.getDrawEngine().draw(this, new DrawGraphics.GraphicsDG(g), 224 getEditorUI(), startOffset, endOffset, 225 getBaseX(allocReadOnly.x), allocReadOnly.y, Integer.MAX_VALUE); 226 227 g.translate(shift,0); 228 g.setClip(oldClipRect); 229 230 }else{ 231 JTextComponent component = getComponent(); 232 if (component!=null){ 233 DrawEngine drawEngine = (DrawEngine)component.getClientProperty(DrawEngine.PreinitializedDrawEngine.class); 234 if (drawEngine != null){ 235 drawEngine.draw(this, new DrawGraphics.GraphicsDG(g), 236 getEditorUI(), startOffset, endOffset, 237 getBaseX(allocReadOnly.x), allocReadOnly.y, Integer.MAX_VALUE); 238 }else{ 239 DrawEngine.getDrawEngine().draw(this, new DrawGraphics.GraphicsDG(g), 240 getEditorUI(), startOffset, endOffset, 241 getBaseX(allocReadOnly.x), allocReadOnly.y, Integer.MAX_VALUE); 242 } 243 244 } 245 } 246 }catch(BadLocationException ble){ 247 ble.printStackTrace(); 248 } 249 } 250 251 public float getPreferredSpan(int axis) { 252 switch (axis) { 253 case Y_AXIS: 254 262 return getEditorUI().getLineHeight(); 263 case X_AXIS: 264 try{ 265 int offset = Math.max(0, getEndOffset() - 1); 266 Shape retShape = modelToView(offset, new Rectangle (), Position.Bias.Forward, false); 267 int ret = retShape.getBounds().x + retShape.getBounds().width; 268 return Math.max(ret, 1f); 269 }catch(BadLocationException ble){ 270 ble.printStackTrace(); 271 } 272 } 273 274 return 1f; 275 } 276 277 private Rectangle getModel2ViewRect(int startOffset, int endOffset, int startX, int startY, int targetOffset){ 278 Rectangle ret = new Rectangle (); 279 ret.y = startY; 280 if (isEstimatedSpan()) { 281 ret.height = getEditorUI().getLineHeight(); 282 ret.x = startX; 283 ret.width = 1; 284 285 } else { try{ 287 ModelToViewDG modelToViewDG = getModelToViewDG(); 288 modelToViewDG.r = ret; DrawEngine.getDrawEngine().draw(this, modelToViewDG, getEditorUI(), 291 startOffset, endOffset, 292 startX, startY, targetOffset); 293 LockView lv = LockView.get(this); 294 if (lv!=null && (lv.getLockThread() != Thread.currentThread())){ 295 throw new IllegalStateException ("View access without view lock"); } 297 modelToViewDG.r = null; 298 }catch(BadLocationException ble){ 300 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ble); 301 } 302 } 303 return ret; 304 } 305 306 public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException { 307 return modelToView(pos, a, b, true); } 309 310 public Shape modelToView(int pos, Shape a, Position.Bias b, boolean exactSpan) throws BadLocationException { 311 if (exactSpan) { setEstimatedSpan(false); 313 } 314 315 Document d = getDocument(); 316 if (!(d instanceof BaseDocument)) { 317 return new Rectangle (); 318 } 319 BaseDocument doc = (BaseDocument)d; 320 if (pos < 0 || pos > doc.getLength()) { 321 throw new BadLocationException ("Invalid offset=" + pos, pos); } 323 324 Rectangle allocReadOnly = (a instanceof Rectangle ) ? (Rectangle )a : a.getBounds(); 326 Rectangle ret = getModel2ViewRect( 327 getStartOffset(), 328 getAdjustedEOLOffset(), 329 getBaseX(allocReadOnly.x), 330 allocReadOnly.y, 331 pos 332 ); 333 334 return ret; 335 } 336 337 public int viewToModel(float x, float y, Shape a, Position.Bias [] biasReturn) { 338 if (isEstimatedSpan()) { 339 return getStartOffset(); 340 } 341 342 int intX = (int)x; 343 int intY = (int)y; 344 if (biasReturn != null) { 345 biasReturn[0] = Position.Bias.Forward; 346 } 347 int pos = getStartOffset(); 348 Rectangle shapeRect = (a!=null) ? a.getBounds() : new Rectangle (); 349 350 try { 351 int eolPos = getAdjustedEOLOffset(); 352 ViewToModelDG viewToModelDG = getViewToModelDG(); 353 viewToModelDG.setTargetX(intX); 355 viewToModelDG.setEOLOffset(eolPos); 356 DrawEngine.getDrawEngine().draw(this, viewToModelDG, getEditorUI(), getStartOffset() , eolPos, 357 getBaseX(0) + shapeRect.x, shapeRect.y, -1); 358 pos = viewToModelDG.getOffset(); 359 return pos; 360 } catch (BadLocationException e) { 362 } 364 return pos; 365 } 366 367 368 final class ViewToModelDG extends DrawGraphics.SimpleDG { 369 370 int targetX; 371 int offset; 372 int eolOffset; 373 374 void setTargetX(int targetX) { 375 this.targetX = targetX; 376 } 377 378 void setEOLOffset(int eolOffset) { 379 this.eolOffset = eolOffset; 380 this.offset = eolOffset; 381 } 382 383 int getOffset() { 384 return offset; 385 } 386 387 public boolean targetOffsetReached(int offset, char ch, int x, 388 int charWidth, DrawContext ctx) { 389 390 if (offset <= eolOffset) { 391 if (x + charWidth < targetX) { 392 this.offset = offset; 393 return true; 394 } else { this.offset = offset; 396 if (targetX > x + charWidth / 2) { 397 Document doc = getDocument(); 398 if (ch != '\n' && doc != null && offset < doc.getLength()) { 399 this.offset++; 400 } 401 } 402 return false; 403 } 404 } 405 return false; 406 } 407 } 408 409 final class ModelToViewDG extends DrawGraphics.SimpleDG { 410 411 Rectangle r; 412 413 public boolean targetOffsetReached(int pos, char ch, int x, 414 int charWidth, DrawContext ctx) { 415 r.x = x; 416 r.y = getY(); 417 r.width = charWidth; 418 r.height = getEditorUI().getLineHeight(); 419 return false; 420 } 421 422 } 423 424 425 public View createFragment(int p0, int p1){ 426 Element elem = getElement(); 427 return p0>=0 && p0>=elem.getStartOffset() && p0<elem.getEndOffset() && 429 p1>0 && p1<=elem.getEndOffset() && p1>elem.getStartOffset() && 430 (p0!=elem.getStartOffset() || p1!=elem.getEndOffset()) ? 432 new FragmentView(getElement(), p0 - elem.getStartOffset(), p1 - p0) : 433 this; 434 } 435 436 public double getLayoutMajorAxisPreferredSpan() { 437 return layoutMajorAxisPreferredSpan; 438 } 439 440 public float getLayoutMajorAxisPreferredSpanFloat() { 441 return layoutMajorAxisPreferredSpan; 442 } 443 444 protected void setLayoutMajorAxisPreferredSpan(float layoutMajorAxisPreferredSpan) { 445 this.layoutMajorAxisPreferredSpan = layoutMajorAxisPreferredSpan; 446 } 447 448 public double getLayoutMajorAxisRawOffset() { 449 return layoutMajorAxisRawOffset; 450 } 451 452 public void setLayoutMajorAxisRawOffset(double layoutMajorAxisRawOffset) { 453 this.layoutMajorAxisRawOffset = layoutMajorAxisRawOffset; 454 } 455 456 public float getLayoutMinorAxisAlignment() { 457 return getAlignment(getMinorAxis()); } 459 460 public float getLayoutMinorAxisMaximumSpan() { 461 return getLayoutMinorAxisPreferredSpan(); 462 } 463 464 public float getLayoutMinorAxisMinimumSpan() { 465 return getLayoutMinorAxisPreferredSpan(); 466 } 467 468 public float getLayoutMinorAxisPreferredSpan() { 469 return layoutMinorAxisPreferredSpan; 470 } 471 472 protected void setLayoutMinorAxisPreferredSpan(float layoutMinorAxisPreferredSpan) { 473 this.layoutMinorAxisPreferredSpan = layoutMinorAxisPreferredSpan; 474 } 475 476 public View getView() { 477 return this; 478 } 479 480 public int getViewRawIndex() { 481 return viewRawIndex; 482 } 483 484 public void setViewRawIndex(int viewRawIndex) { 485 this.viewRawIndex = viewRawIndex; 486 } 487 488 public boolean isFlyweight() { 489 return false; 490 } 491 492 public ViewLayoutState selectLayoutMajorAxis(int majorAxis) { 493 495 if (majorAxis == View.X_AXIS) { 496 setStatusBits(X_MAJOR_AXIS_BIT); 497 } else { clearStatusBits(X_MAJOR_AXIS_BIT); 499 } 500 501 return this; 502 } 503 504 protected final ViewLayoutState.Parent getLayoutStateParent() { 505 View parent = getView().getParent(); 506 return (parent instanceof ViewLayoutState.Parent) 507 ? ((ViewLayoutState.Parent)parent) 508 : null; 509 } 510 511 public void updateLayout() { 512 if (isLayoutValid()) { 514 return; } 516 517 ViewLayoutState.Parent lsParent = getLayoutStateParent(); 518 if (lsParent == null) { 519 return; 520 } 521 522 if (isStatusBitsNonZero(MINOR_AXIS_PREFERENCE_CHANGED_BIT)) { clearStatusBits(MINOR_AXIS_PREFERENCE_CHANGED_BIT); 525 526 int minorAxis = getMinorAxis(); 527 if (minorAxisUpdateLayout(minorAxis)) { 528 lsParent.minorAxisPreferenceChanged(this); 529 } 530 } 531 532 if (isStatusBitsNonZero(MAJOR_AXIS_PREFERENCE_CHANGED_BIT)) { clearStatusBits(MAJOR_AXIS_PREFERENCE_CHANGED_BIT); 535 536 float oldSpan = getLayoutMajorAxisPreferredSpanFloat(); 537 float newSpan = getPreferredSpan(getMajorAxis()); 538 setLayoutMajorAxisPreferredSpan(newSpan); 539 double majorAxisSpanDelta = newSpan - oldSpan; 540 if (majorAxisSpanDelta != 0) { 541 lsParent.majorAxisPreferenceChanged(this, majorAxisSpanDelta); 542 } 543 } 544 545 if (isStatusBitsNonZero(VIEW_SIZE_INVALID_BIT)) { 547 clearStatusBits(VIEW_SIZE_INVALID_BIT); 548 549 float width; 550 float height; 551 float majorAxisSpan = (float)getLayoutMajorAxisPreferredSpan(); 552 float minorAxisSpan = lsParent.getMinorAxisSpan(this); 553 if (isXMajorAxis()) { width = majorAxisSpan; 555 height = minorAxisSpan; 556 } else { 557 width = minorAxisSpan; 558 height = majorAxisSpan; 559 } 560 561 setSize(width, height); 562 } 563 564 updateLayout(); 566 } 567 568 protected boolean minorAxisUpdateLayout(int minorAxis) { 569 boolean minorAxisPreferenceChanged = false; 570 float val; 571 572 val = getPreferredSpan(minorAxis); 573 if (val != getLayoutMinorAxisPreferredSpan()) { 574 setLayoutMinorAxisPreferredSpan(val); 575 minorAxisPreferenceChanged = true; 576 } 577 578 return minorAxisPreferenceChanged; 579 } 580 581 public void viewPreferenceChanged(boolean width, boolean height) { 582 if (isXMajorAxis()) { if (width) { 584 setStatusBits(MAJOR_AXIS_PREFERENCE_CHANGED_BIT); } 586 if (height) { 587 setStatusBits(MINOR_AXIS_PREFERENCE_CHANGED_BIT); } 589 } else { 590 if (width) { 591 setStatusBits(MINOR_AXIS_PREFERENCE_CHANGED_BIT); } 593 if (height) { 594 setStatusBits(MAJOR_AXIS_PREFERENCE_CHANGED_BIT); } 596 } 597 setStatusBits(VIEW_SIZE_INVALID_BIT); } 599 600 public void markViewSizeInvalid() { 601 setStatusBits(VIEW_SIZE_INVALID_BIT); 602 } 603 604 public boolean isLayoutValid() { 605 return !isStatusBitsNonZero(ANY_INVALID); 606 } 607 608 protected final boolean isXMajorAxis() { 609 return isStatusBitsNonZero(X_MAJOR_AXIS_BIT); 610 } 611 612 protected final int getMajorAxis() { 613 return isXMajorAxis() ? View.X_AXIS : View.Y_AXIS; 614 } 615 616 protected final int getMinorAxis() { 617 return isXMajorAxis() ? View.Y_AXIS : View.X_AXIS; 618 } 619 620 protected final int getStatusBits(int bits) { 621 return (statusBits & bits); 622 } 623 624 protected final boolean isStatusBitsNonZero(int bits) { 625 return (getStatusBits(bits) != 0); 626 } 627 628 protected final void setStatusBits(int bits) { 629 statusBits |= bits; 630 } 631 632 protected final void clearStatusBits(int bits) { 633 statusBits &= ~bits; 634 } 635 636 637 638 static class FragmentView extends DrawEngineLineView{ 639 640 private Position startPos; 641 private Position endPos; 642 643 public FragmentView(Element elem, int offset, int length){ 644 super(elem); 645 try { 646 Document doc = elem.getDocument(); 647 this.startPos = doc.createPosition(super.getStartOffset() + offset); 648 this.endPos = doc.createPosition(startPos.getOffset() + length); 649 } catch (BadLocationException e) { 650 ErrorManager.getDefault().notify(e); 651 } 652 } 653 654 protected boolean isFragment(){ 655 return true; 656 } 657 658 public int getStartOffset() { 659 return startPos.getOffset(); 660 } 661 662 public int getEndOffset() { 663 return endPos.getOffset(); 664 } 665 666 } 667 668 } 669 | Popular Tags |