| 1 21 24 package org.lobobrowser.html.gui; 25 26 import java.awt.*; 27 import java.awt.event.*; 28 import java.awt.datatransfer.*; 29 30 import javax.swing.*; 31 32 import org.lobobrowser.html.*; 33 import org.lobobrowser.html.domimpl.*; 34 import org.lobobrowser.html.renderer.*; 35 import org.lobobrowser.util.*; 36 import org.lobobrowser.util.gui.ColorFactory; 37 import org.w3c.dom.*; 38 import java.util.logging.*; 39 import java.util.*; 40 41 49 public class HtmlBlockPanel extends JComponent implements NodeRenderer, RenderableContainer, ClipboardOwner { 50 private static final Logger logger = Logger.getLogger(HtmlBlockPanel.class.getName()); 51 private static final boolean loggableInfo = logger.isLoggable(Level.INFO); 52 protected final FrameContext frameContext; 53 protected final UserAgentContext ucontext; 54 protected final HtmlRendererContext rcontext; 55 56 protected RenderableSpot startSelection; 57 protected RenderableSpot endSelection; 58 protected RBlock rblock; 59 protected int preferredWidth = -1; 60 61 public HtmlBlockPanel(int listNesting, UserAgentContext pcontext, HtmlRendererContext rcontext, FrameContext frameContext) { 62 this(listNesting, ColorFactory.TRANSPARENT, false, pcontext, rcontext, frameContext); 63 } 64 65 public HtmlBlockPanel(int listNesting, Color background, boolean opaque, UserAgentContext pcontext, HtmlRendererContext rcontext, FrameContext frameContext) { 66 this.setLayout(null); 67 this.setAutoscrolls(true); 68 this.frameContext = frameContext; 69 this.ucontext = pcontext; 70 this.rcontext = rcontext; 71 this.setOpaque(opaque); 72 this.setBackground(background); 73 ActionListener actionListener = new ActionListener() { 74 public void actionPerformed(ActionEvent e) { 75 String command = e.getActionCommand(); 76 if("copy".equals(command)) { 77 copy(); 78 } 79 } 80 }; 81 this.registerKeyboardAction(actionListener, "copy", KeyStroke.getKeyStroke(KeyEvent.VK_COPY, 0), JComponent.WHEN_FOCUSED); 82 this.registerKeyboardAction(actionListener, "copy", KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), JComponent.WHEN_FOCUSED); 83 this.addMouseListener(new MouseListener() { 84 public void mouseClicked(MouseEvent e) { 85 onMouseClick(e); 86 } 87 public void mouseEntered(MouseEvent e) { 88 } 89 public void mouseExited(MouseEvent e) { 90 onMouseExited(e); 91 } 92 public void mousePressed(MouseEvent e) { 93 onMousePressed(e); 94 } 95 public void mouseReleased(MouseEvent e) { 96 onMouseReleased(e); 97 } 98 }); 99 this.addMouseMotionListener(new MouseMotionListener() { 100 103 public void mouseDragged(MouseEvent e) { 104 onMouseDragged(e); 105 } 106 107 110 public void mouseMoved(MouseEvent arg0) { 111 } 112 }); 113 this.addMouseWheelListener(new MouseWheelListener() { 114 public void mouseWheelMoved(MouseWheelEvent e) { 115 onMouseWheelMoved(e); 116 } 117 }); 118 } 119 120 public BoundableRenderable getRootRenderable() { 121 return this.rblock; 122 } 123 124 131 public void setPreferredWidth(int width) { 132 this.preferredWidth = width; 133 } 134 135 142 public Dimension getPreferredSize() { 143 if(this.isPreferredSizeSet()) { 145 return super.getPreferredSize(); 146 } 147 final int pw = this.preferredWidth; 148 if(pw != -1) { 149 final RBlock block = this.rblock; 150 if(block != null) { 151 block.layout(pw, 0, false, false, RBlock.OVERFLOW_NONE); 154 int newPw = Math.max(block.width + block.getVScrollBarWidth(), pw); 156 return new Dimension(newPw, block.height); 157 } 158 } 159 return new Dimension(600, 400); 160 } 161 162 public void finalize() throws Throwable { 163 super.finalize(); 164 } 165 166 public boolean copy() { 167 String selection = HtmlBlockPanel.this.getSelectionText(); 168 if(selection != null) { 169 Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); 170 clipboard.setContents(new StringSelection(selection), HtmlBlockPanel.this); 171 return true; 172 } 173 else { 174 return false; 175 } 176 } 177 178 private Insets defaultPaddingInsets = null; 179 180 public void setDefaultPaddingInsets(Insets insets) { 181 this.defaultPaddingInsets = insets; 182 } 183 184 public int getFirstLineHeight() { 185 RBlock block = this.rblock; 186 return block == null ? 0 : block.getFirstLineHeight(); 187 } 188 189 public void setSelectionEnd(RenderableSpot rpoint) { 190 this.endSelection = rpoint; 191 } 192 193 public void setSelectionStart(RenderableSpot rpoint) { 194 this.startSelection = rpoint; 195 } 196 197 public boolean isSelectionAvailable() { 198 RenderableSpot start = this.startSelection; 199 RenderableSpot end = this.endSelection; 200 return start != null && end != null && !start.equals(end); 201 } 202 203 public org.w3c.dom.Node getSelectionNode() { 204 RenderableSpot start = this.startSelection; 205 RenderableSpot end = this.endSelection; 206 if(start != null && end != null) { 207 return Nodes.getCommonAncestor((Node) start.renderable.getModelNode(), (Node) end.renderable.getModelNode()); 208 } 209 else { 210 return null; 211 } 212 } 213 214 218 public void setRootNode(NodeImpl node) { 219 if(node != null) { 220 RBlock block = new RBlock(node, 0, this.ucontext, this.rcontext, this.frameContext, this, RBlock.OVERFLOW_VERTICAL); 221 block.setDefaultPaddingInsets(this.defaultPaddingInsets); 222 node.setUINode(block); 223 this.rblock = block; 224 } 225 else { 226 this.rblock = null; 227 } 228 this.invalidate(); 229 this.validateAll(); 230 this.repaint(); 231 } 232 233 protected void validateAll() { 234 Component toValidate = this; 235 for(;;) { 236 Container parent = toValidate.getParent(); 237 if(parent == null || parent.isValid()) { 238 break; 239 } 240 toValidate = parent; 241 } 242 toValidate.validate(); 243 } 244 245 248 public void setRootNode(NodeImpl node, boolean setContainer) { 249 this.setRootNode(node); 250 } 251 252 protected void revalidatePanel() { 253 this.invalidate(); 255 this.validate(); 256 this.repaint(); 258 } 259 260 public NodeImpl getRootNode() { 261 RBlock block = this.rblock; 262 return block == null ? null : (NodeImpl) block.getModelNode(); 263 } 264 265 private void onMouseClick(MouseEvent event) { 266 RBlock block = this.rblock; 268 if(block != null) { 269 if(event.getButton() == MouseEvent.BUTTON1 && event.getClickCount() > 1) { 270 Point point = event.getPoint(); 271 block.onDoubleClick(event, point.x, point.y); 272 273 } 279 } 280 } 281 282 private BoundableRenderable mousePressTarget; 283 284 private void onMousePressed(MouseEvent event) { 285 this.requestFocus(); 286 RBlock block = this.rblock; 287 if(block != null) { 288 Point point = event.getPoint(); 289 this.mousePressTarget = block; 290 int rx = point.x; 291 int ry = point.y; 292 block.onMousePressed(event, point.x, point.y); 293 RenderableSpot rp = block.getLowestRenderableSpot(rx, ry); 294 if(rp != null) { 295 this.frameContext.resetSelection(rp); 296 } 297 else { 298 this.frameContext.resetSelection(null); 299 } 300 301 } 320 } 321 322 private void onMouseReleased(MouseEvent event) { 323 RBlock block = this.rblock; 324 if(block != null) { 325 Point point = event.getPoint(); 326 int rx = point.x; 327 int ry = point.y; 328 if(event.getButton() == MouseEvent.BUTTON1) { 329 block.onMouseClick(event, rx, ry); 331 } 332 block.onMouseReleased(event, rx, ry); 333 334 348 BoundableRenderable oldTarget = this.mousePressTarget; 349 if(oldTarget != null) { 350 this.mousePressTarget = null; 351 if(oldTarget != block) { 352 oldTarget.onMouseDisarmed(event); 353 } 354 } 355 } 356 else { 357 this.mousePressTarget = null; 358 } 359 } 360 361 private void onMouseExited(MouseEvent event) { 362 BoundableRenderable oldTarget = this.mousePressTarget; 363 if(oldTarget != null) { 364 this.mousePressTarget = null; 365 oldTarget.onMouseDisarmed(event); 366 } 367 } 368 369 private void onMouseWheelMoved(MouseWheelEvent mwe) { 370 RBlock block = this.rblock; 371 if(block != null) { 372 switch(mwe.getScrollType()) { 373 case MouseWheelEvent.WHEEL_UNIT_SCROLL: 374 int units = mwe.getWheelRotation() * mwe.getScrollAmount(); 375 block.scrollByUnits(JScrollBar.VERTICAL, units); 376 break; 377 } 378 } 379 } 380 381 private void onMouseDragged(MouseEvent event) { 382 RBlock block = this.rblock; 383 if(block != null) { 384 Point point = event.getPoint(); 385 RenderableSpot rp = block.getLowestRenderableSpot(point.x, point.y); 386 if(rp != null) { 387 this.frameContext.expandSelection(rp); 388 } 389 block.ensureVisible(point); 390 } 391 } 392 393 396 public void paint(Graphics g) { 398 if(this.isOpaque()) { 403 Rectangle clipBounds = g.getClipBounds(); 405 g.setColor(this.getBackground()); 406 g.fillRect(clipBounds.x, clipBounds.y, clipBounds.width, clipBounds.height); 407 } 408 if(g instanceof Graphics2D) { 409 Graphics2D g2 = (Graphics2D) g; 410 g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); 411 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 412 } 413 RBlock block = this.rblock; 414 if(block != null) { 415 boolean liflag = loggableInfo; 416 long time1 = liflag ? System.currentTimeMillis() : 0; 417 block.paint(g); 418 if(liflag) { 419 long time2 = System.currentTimeMillis(); 420 Node rootNode = this.getRootNode(); 421 String uri = rootNode instanceof Document ? ((Document) rootNode).getDocumentURI() : ""; 422 logger.info("paintComponent(): URI=[" + uri + "]. Block paint elapsed: " + (time2 - time1) + " ms."); 423 } 424 425 427 RenderableSpot start = this.startSelection; 428 RenderableSpot end = this.endSelection; 429 if(start != null && end != null && !start.equals(end)) { 430 block.paintSelection(g, false, start, end); 431 } 432 } 433 } 434 435 436 public void doLayout() { 437 if(EventQueue.isDispatchThread()) { 438 Dimension size = this.getSize(); 439 boolean liflag = loggableInfo; 440 long time1 = 0; 441 if(liflag) { 442 time1 = System.currentTimeMillis(); 443 } 444 this.removeAll(); 445 RBlock block = this.rblock; 446 if(block != null) { 447 ModelNode rootNode = block.getModelNode(); 448 block.layout(size.width, size.height, true, true); 449 block.setOrigin(0, 0); 452 block.updateWidgetBounds(0, 0); 453 if(liflag) { 454 long time2 = System.currentTimeMillis(); 455 String uri = rootNode instanceof Document ? ((Document) rootNode).getDocumentURI() : ""; 456 logger.info("doLayout(): URI=[" + uri + "]. Block layout elapsed: " + (time2 - time1) + " ms. Component count: " + this.getComponentCount() + "."); 457 } 458 } 459 } 460 else { 461 EventQueue.invokeLater(new Runnable () { 462 public void run() { 463 HtmlBlockPanel.this.doLayout(); 464 } 465 }); 466 } 467 } 468 469 472 public void repaint(ModelNode modelNode) { 473 this.repaint(); 475 } 476 477 public String getSelectionText() { 478 RenderableSpot start = this.startSelection; 479 RenderableSpot end = this.endSelection; 480 if(start != null && end != null) { 481 StringBuffer buffer = new StringBuffer (); 482 this.rblock.extractSelectionText(buffer, false, start, end); 483 return buffer.toString(); 484 } 485 else { 486 return null; 487 } 488 } 489 490 public boolean hasSelection() { 491 RenderableSpot start = this.startSelection; 492 RenderableSpot end = this.endSelection; 493 if(start != null && end != null && !start.equals(end)) { 494 return true; 495 } 496 else { 497 return false; 498 } 499 } 500 501 protected void paintChildren(Graphics g) { 502 } 506 507 public Color getPaintedBackgroundColor() { 508 return this.isOpaque() ? this.getBackground() : null; 509 } 510 511 514 public void lostOwnership(Clipboard arg0, Transferable arg1) { 515 } 516 517 public void relayout() { 518 this.revalidatePanel(); 522 } 523 524 public void invalidateLayoutUpTree() { 525 } 530 531 public void updateAllWidgetBounds() { 532 this.rblock.updateWidgetBounds(0, 0); 533 } 534 535 public Point getGUIPoint(int clientX, int clientY) { 536 return new Point(clientX, clientY); 538 } 539 540 public void focus() { 541 this.grabFocus(); 542 } 543 544 private boolean processingDocumentNotification = false; 545 546 void processDocumentNotifications(DocumentNotification[] notifications) { 547 if(this.processingDocumentNotification) { 549 throw new IllegalStateException ("Recursive"); 554 } 555 this.processingDocumentNotification = true; 556 try { 557 boolean topLayout = false; 561 java.util.ArrayList repainters = null; 562 int length = notifications.length; 563 for(int i = 0; i < length; i++) { 564 DocumentNotification dn = notifications[i]; 565 int type = dn.type; 566 switch(type) { 567 case DocumentNotification.GENERIC: 568 case DocumentNotification.SIZE: { 569 NodeImpl node = dn.node; 570 if(node == null) { 571 if(loggableInfo) { 573 logger.info("processDocumentNotifications(): Calling invalidateLayoutDeep()."); 574 } 575 this.rblock.invalidateLayoutDeep(); 576 } 578 else { 579 UINode uiNode = node.findUINode(); 580 if(uiNode != null) { 581 RElement relement = (RElement) uiNode; 582 relement.invalidateLayoutUpTree(); 583 } 587 else { 588 if(loggableInfo) { 589 logger.info("processDocumentNotifications(): Unable to find UINode for " + node); 590 } 591 } 592 } 593 topLayout = true; 594 break; 595 } 596 case DocumentNotification.POSITION: { 597 NodeImpl node = dn.node; 599 NodeImpl parent = (NodeImpl) node.getParentNode(); 600 if(parent != null) { 601 UINode uiNode = parent.findUINode(); 602 if(uiNode != null) { 603 RElement relement = (RElement) uiNode; 604 relement.invalidateLayoutUpTree(); 605 } 606 } 607 topLayout = true; 608 break; 609 } 610 case DocumentNotification.LOOK: { 611 NodeImpl node = dn.node; 612 UINode uiNode = node.findUINode(); 613 if(uiNode != null) { 614 if(repainters == null) { 615 repainters = new ArrayList(1); 616 } 617 RElement relement = (RElement) uiNode; 618 repainters.add(relement); 620 } 621 break; 622 } 623 default: 624 break; 625 } 626 } 627 if(topLayout) { 628 this.revalidatePanel(); 629 } 630 else { 631 if(repainters != null) { 632 Iterator i = repainters.iterator(); 633 while(i.hasNext()) { 634 RElement element = (RElement) i.next(); 635 element.repaint(); 636 } 637 } 638 } 639 } finally { 640 this.processingDocumentNotification = false; 641 } 642 } 643 644 public void addDelayedPair(DelayedPair pair) { 645 } 647 648 public RenderableContainer getParentContainer() { 649 return null; 650 } 651 652 public Collection getDelayedPairs() { 653 return null; 654 } 655 656 public void clearDelayedPairs() { 657 } 658 } | Popular Tags |