1 21 24 package org.lobobrowser.html.renderer; 25 26 import java.awt.*; 27 import java.util.*; 28 29 import org.lobobrowser.html.domimpl.ModelNode; 30 import org.lobobrowser.html.style.RenderState; 31 32 35 class RLine extends BaseRCollection { 36 private final ArrayList renderables = new ArrayList(8); 37 private int baseLineOffset; 39 private int desiredMaxWidth; 40 41 public RLine(ModelNode modelNode, RenderableContainer container, int x, int y, int desiredMaxWidth, int height) { 42 super(container, modelNode); 45 this.x = x; 46 this.y = y; 47 this.width = 0; 48 this.height = height; 49 this.desiredMaxWidth = desiredMaxWidth; 50 this.layoutUpTreeCanBeInvalidated = true; 52 } 53 54 public int getBaselineOffset() { 55 return this.baseLineOffset; 56 } 57 58 protected void invalidateLayoutLocal() { 59 this.layoutUpTreeCanBeInvalidated = true; 62 } 63 64 67 68 public void paint(Graphics g) { 69 RenderState rs = this.modelNode.getRenderState(); 71 if(rs != null) { 72 Color textColor = rs.getColor(); 73 g.setColor(textColor); 74 Font font = rs.getFont(); 75 g.setFont(font); 76 } 77 Iterator i = this.renderables.iterator(); 80 if(i != null) { 81 while(i.hasNext()) { 82 Object r = i.next(); 83 if(r instanceof RElement) { 84 RElement relement = (RElement) r; 86 Graphics newG = g.create(relement.getX(), relement.getY(), relement.getWidth(), relement.getHeight()); 87 try { 88 relement.paint(newG); 89 } finally { 90 newG.dispose(); 91 } 92 } 93 else if(r instanceof BoundableRenderable) { 94 BoundableRenderable br = (BoundableRenderable) r; 95 br.paintTranslated(g); 96 } 97 else { 98 ((Renderable) r).paint(g); 99 } 100 } 101 } 102 } 103 104 public final void addStyleChanger(RStyleChanger sc) { 105 this.renderables.add(sc); 106 } 107 108 113 public final void add(Renderable renderable, boolean allowOverflow) throws OverflowException { 114 if(renderable instanceof RWord) { 115 this.addWord((RWord) renderable, allowOverflow); 116 } 117 else if(renderable instanceof RBlank) { 118 this.addBlank((RBlank) renderable); 119 } 120 else if(renderable instanceof RElement) { 121 this.addElement((RElement) renderable, allowOverflow); 122 } 123 else if(renderable instanceof RSpacing) { 124 this.addSpacing((RSpacing) renderable); 125 } 126 else if(renderable instanceof RStyleChanger) { 127 this.addStyleChanger((RStyleChanger) renderable); 128 } 129 else { 130 throw new IllegalArgumentException ("Can't add " + renderable); 131 } 132 } 133 134 public final void addWord(RWord rword, boolean allowOverflow) throws OverflowException { 135 int offset = this.width; 137 int wiwidth = rword.width; 138 if(!allowOverflow && (offset != 0 && offset + wiwidth > this.desiredMaxWidth)) { 139 ArrayList renderables = this.renderables; 140 ArrayList overflow = null; 141 boolean cancel = false; 142 for(int i = renderables.size(); --i >= 0;) { 147 Renderable renderable = (Renderable) renderables.get(i); 148 if(renderable instanceof RWord || !(renderable instanceof BoundableRenderable)) { 149 if(overflow == null) { 150 overflow = new ArrayList(); 151 } 152 if(renderable != rword && renderable instanceof RWord && ((RWord) renderable).getX() == 0) { 153 cancel = true; 156 break; 158 } 159 if(renderable instanceof RWord) { 160 int newOffset = ((RWord) renderable).getBounds().x; 161 this.width = newOffset; 162 } 163 overflow.add(0, renderable); 164 renderables.remove(i); 165 } 166 else { 167 break; 168 } 169 } 170 if(!cancel) { 171 if(overflow == null) { 172 throw new OverflowException(Collections.singleton(rword)); 173 } 174 else { 175 overflow.add(rword); 176 throw new OverflowException(overflow); 177 } 178 } 179 } 180 181 183 int extraHeight = 0; 184 int maxDescent = this.height - this.baseLineOffset; 185 if(rword.descent > maxDescent) { 186 extraHeight += (rword.descent - maxDescent); 187 } 188 int maxAscentPlusLeading = this.baseLineOffset; 189 if(rword.ascentPlusLeading > maxAscentPlusLeading) { 190 extraHeight += (rword.ascentPlusLeading - maxAscentPlusLeading); 191 } 192 if(extraHeight > 0) { 193 int newHeight = this.height + extraHeight; 194 this.adjustHeight(newHeight, newHeight, RElement.VALIGN_ABSBOTTOM); 195 } 196 this.renderables.add(rword); 197 rword.setParent(this); 198 int x = offset; 199 offset += wiwidth; 200 this.width = offset; 201 rword.setOrigin(x, this.baseLineOffset - rword.ascentPlusLeading); 202 } 203 204 public final void addBlank(RBlank rblank) { 205 int x = this.width; 207 int width = rblank.width; 208 rblank.setOrigin(x, this.baseLineOffset - rblank.ascentPlusLeading); 209 this.renderables.add(rblank); 210 rblank.setParent(this); 211 this.width = x + width; 212 } 213 214 public final void addSpacing(RSpacing rblank) { 215 int x = this.width; 217 int width = rblank.width; 218 rblank.setOrigin(x, (this.height - rblank.height) / 2); 219 this.renderables.add(rblank); 220 rblank.setParent(this); 221 this.width = x + width; 222 } 223 224 231 private final void setElementY(RElement relement, int elementHeight, int valign) { 232 int yoffset; 234 switch(valign) { 235 case RElement.VALIGN_ABSBOTTOM: 236 yoffset = this.height - elementHeight; 237 break; 238 case RElement.VALIGN_ABSMIDDLE: 239 yoffset = (this.height - elementHeight) / 2; 240 break; 241 case RElement.VALIGN_BASELINE: 242 case RElement.VALIGN_BOTTOM: 243 yoffset = this.baseLineOffset - elementHeight; 244 break; 245 case RElement.VALIGN_MIDDLE: 246 yoffset = this.baseLineOffset - elementHeight / 2; 247 break; 248 case RElement.VALIGN_TOP: 249 yoffset = 0; 250 break; 251 default: 252 yoffset = this.baseLineOffset - elementHeight; 253 } 254 relement.setY(yoffset); 257 } 258 259 public final void addElement(RElement relement, boolean allowOverflow) throws OverflowException { 260 int boundsw = this.width; 262 int desiredMaxWidth = this.desiredMaxWidth; 263 int pw = relement.getWidth(); 267 int offset = boundsw; 268 if(!allowOverflow && (offset != 0 && offset + pw > desiredMaxWidth)) { 269 throw new OverflowException(Collections.singleton(relement)); 270 } 271 int boundsh = this.height; 274 int ph = relement.getHeight(); 275 int requiredHeight; 276 int valign = relement.getVAlign(); 277 switch(valign) { 278 case RElement.VALIGN_BASELINE: 279 case RElement.VALIGN_BOTTOM: 280 requiredHeight = ph + (boundsh - this.baseLineOffset); 281 break; 282 case RElement.VALIGN_MIDDLE: 283 requiredHeight = Math.max(ph, ph / 2 + (boundsh - this.baseLineOffset)); 284 break; 285 default: 286 requiredHeight = ph; 287 break; 288 } 289 if(requiredHeight > boundsh) { 290 this.adjustHeight(requiredHeight, ph, valign); 292 } 293 this.renderables.add(relement); 294 relement.setParent(this); 295 relement.setX(offset); 296 this.setElementY(relement, ph, valign); 297 int newX = offset + pw; 298 this.width = newX; 299 } 300 301 362 369 private void adjustHeight(int newHeight, int elementHeight, int valign) { 370 this.height = newHeight; 373 ArrayList renderables = this.renderables; 374 FontMetrics firstFm = this.modelNode.getRenderState().getFontMetrics(); 376 int maxDescent = firstFm.getDescent(); 377 int maxAscentPlusLeading = firstFm.getAscent() + firstFm.getLeading(); 378 for(Iterator i = renderables.iterator(); i.hasNext();) { 379 Object r = i.next(); 380 if(r instanceof RStyleChanger) { 381 RStyleChanger rstyleChanger = (RStyleChanger) r; 382 FontMetrics fm = rstyleChanger.getModelNode().getRenderState().getFontMetrics(); 383 int descent = fm.getDescent(); 384 if(descent > maxDescent) { 385 maxDescent = descent; 386 } 387 int ascentPlusLeading = fm.getAscent() + fm.getLeading(); 388 if(ascentPlusLeading > maxAscentPlusLeading) { 389 maxAscentPlusLeading = ascentPlusLeading; 390 } 391 } 392 } 393 int textHeight = maxDescent + maxAscentPlusLeading; 394 395 398 int baseline; 399 switch(valign) { 400 case RElement.VALIGN_ABSBOTTOM: 401 baseline = newHeight - maxDescent; 402 break; 403 case RElement.VALIGN_ABSMIDDLE: 404 baseline = (newHeight + textHeight) / 2 - maxDescent; 405 break; 406 case RElement.VALIGN_BASELINE: 407 case RElement.VALIGN_BOTTOM: 408 baseline = elementHeight; 409 break; 410 case RElement.VALIGN_MIDDLE: 411 baseline = newHeight / 2; 412 break; 413 case RElement.VALIGN_TOP: 414 baseline = maxAscentPlusLeading; 415 break; 416 default: 417 baseline = elementHeight; 418 break; 419 } 420 this.baseLineOffset = baseline; 421 422 for(Iterator i = renderables.iterator(); i.hasNext();) { 424 Object r = i.next(); 425 if(r instanceof RWord) { 426 RWord rword = (RWord) r; 427 rword.setY(baseline - rword.ascentPlusLeading); 428 } 429 else if(r instanceof RBlank) { 430 RBlank rblank = (RBlank) r; 431 rblank.setY(baseline - rblank.ascentPlusLeading); 432 } 433 else if(r instanceof RElement) { 434 RElement relement = (RElement) r; 435 this.setElementY(relement, relement.getHeight(), relement.getVAlign()); 437 } 438 else { 439 } 441 } 442 } 444 445 public boolean onMouseClick(java.awt.event.MouseEvent event, int x, int y) { 446 Renderable[] rarray = (Renderable[]) this.renderables.toArray(Renderable.EMPTY_ARRAY); 447 BoundableRenderable r = MarkupUtilities.findRenderable(rarray, x, y, false); 448 if(r != null) { 449 Rectangle rbounds = r.getBounds(); 450 return r.onMouseClick(event, x - rbounds.x, y - rbounds.y); 451 } 452 else { 453 return true; 454 } 455 } 456 457 public boolean onDoubleClick(java.awt.event.MouseEvent event, int x, int y) { 458 Renderable[] rarray = (Renderable[]) this.renderables.toArray(Renderable.EMPTY_ARRAY); 459 BoundableRenderable r = MarkupUtilities.findRenderable(rarray, x, y, false); 460 if(r != null) { 461 Rectangle rbounds = r.getBounds(); 462 return r.onDoubleClick(event, x - rbounds.x, y - rbounds.y); 463 } 464 else { 465 return true; 466 } 467 } 468 469 private BoundableRenderable mousePressTarget; 470 471 public boolean onMousePressed(java.awt.event.MouseEvent event, int x, int y) { 472 Renderable[] rarray = (Renderable[]) this.renderables.toArray(Renderable.EMPTY_ARRAY); 473 BoundableRenderable r = MarkupUtilities.findRenderable(rarray, x, y, false); 474 if(r != null) { 475 this.mousePressTarget = r; 476 Rectangle rbounds = r.getBounds(); 477 return r.onMousePressed(event, x - rbounds.x, y - rbounds.y); 478 } 479 else { 480 return true; 481 } 482 } 483 484 public RenderableSpot getLowestRenderableSpot(int x, int y) { 485 Renderable[] rarray = (Renderable[]) this.renderables.toArray(Renderable.EMPTY_ARRAY); 486 BoundableRenderable br = MarkupUtilities.findRenderable(rarray, x, y, false); 487 if(br != null) { 488 Rectangle rbounds = br.getBounds(); 489 return br.getLowestRenderableSpot(x - rbounds.x, y - rbounds.y); 490 } 491 else { 492 return new RenderableSpot(this, x, y); 493 } 494 } 495 496 public boolean onMouseReleased(java.awt.event.MouseEvent event, int x, int y) { 497 Renderable[] rarray = (Renderable[]) this.renderables.toArray(Renderable.EMPTY_ARRAY); 498 BoundableRenderable r = MarkupUtilities.findRenderable(rarray, x, y, false); 499 if(r != null) { 500 Rectangle rbounds = r.getBounds(); 501 BoundableRenderable oldArmedRenderable = this.mousePressTarget; 502 if(oldArmedRenderable != null && r != oldArmedRenderable) { 503 oldArmedRenderable.onMouseDisarmed(event); 504 this.mousePressTarget = null; 505 } 506 return r.onMouseReleased(event, x - rbounds.x, y - rbounds.y); 507 } 508 else { 509 BoundableRenderable oldArmedRenderable = this.mousePressTarget; 510 if(oldArmedRenderable != null) { 511 oldArmedRenderable.onMouseDisarmed(event); 512 this.mousePressTarget = null; 513 } 514 return true; 515 } 516 } 517 518 public boolean onMouseDisarmed(java.awt.event.MouseEvent event) { 519 BoundableRenderable target = this.mousePressTarget; 520 if(target != null) { 521 this.mousePressTarget = null; 522 return target.onMouseDisarmed(event); 523 } 524 else { 525 return true; 526 } 527 } 528 529 public Color getBlockBackgroundColor() { 530 return this.container.getPaintedBackgroundColor(); 531 } 532 533 public final void adjustHorizontalBounds(int newX, int newMaxWidth) throws OverflowException { 534 this.x = newX; 535 this.desiredMaxWidth = newMaxWidth; 536 int topX = newX + newMaxWidth; 537 ArrayList renderables = this.renderables; 538 int size = renderables.size(); 539 ArrayList overflown = null; 540 Rectangle lastInLine = null; 541 for(int i = 0; i < size; i++) { 542 Object r = renderables.get(i); 543 if(overflown == null) { 544 if(r instanceof BoundableRenderable) { 545 BoundableRenderable br = (BoundableRenderable) r; 546 Rectangle brb = br.getBounds(); 547 int x2 = brb.x + brb.width; 548 if(x2 > topX) { 549 overflown = new ArrayList(1); 550 } 551 else { 552 lastInLine = brb; 553 } 554 } 555 } 556 557 if(overflown != null) { 558 overflown.add(r); 560 renderables.remove(i--); 561 size--; 562 } 563 } 564 if(overflown != null) { 565 if(lastInLine != null) { 566 this.width = lastInLine.x + lastInLine.width; 567 } 568 throw new OverflowException(overflown); 569 } 570 } 571 572 665 public Iterator getRenderables() { 666 return this.renderables.iterator(); 667 } 668 } 669 | Popular Tags |