1 21 24 package org.lobobrowser.html.gui; 25 26 import java.awt.*; 27 import java.awt.event.ActionEvent ; 28 import java.util.*; 29 import java.io.*; 30 import javax.swing.*; 31 32 import org.lobobrowser.html.*; 33 import org.lobobrowser.html.domimpl.*; 34 import org.lobobrowser.html.parser.*; 35 import org.lobobrowser.html.renderer.*; 36 import org.lobobrowser.util.EventDispatch2; 37 import org.lobobrowser.util.gui.WrapperLayout; 38 import org.w3c.dom.Document ; 39 import org.w3c.dom.Text ; 40 import org.w3c.dom.html2.*; 41 42 47 public class HtmlPanel extends JComponent implements FrameContext { 48 private final EventDispatch2 selectionDispatch = new SelectionDispatch(); 49 private final javax.swing.Timer notificationTimer; 50 private final DocumentNotificationListener notificationListener; 51 private static final int NOTIF_TIMER_DELAY = 300; 52 53 private volatile boolean isFrameSet = false; 54 private volatile NodeRenderer nodeRenderer = null; 55 private volatile NodeImpl rootNode; 56 private volatile HtmlBlockPanel htmlBlock; 57 private volatile FrameSetPanel frameSetPanel; 58 private volatile int preferredWidth = -1; 59 60 63 public HtmlPanel() { 64 super(); 65 this.setLayout(WrapperLayout.getInstance()); 66 this.setOpaque(false); 67 this.notificationTimer = new javax.swing.Timer (NOTIF_TIMER_DELAY, new NotificationTimerAction()); 68 this.notificationTimer.setRepeats(false); 69 this.notificationListener = new LocalDocumentNotificationListener(); 70 } 71 72 86 public void setPreferredWidth(int width) { 87 this.preferredWidth = width; 88 HtmlBlockPanel htmlBlock = this.htmlBlock; 89 if(htmlBlock != null) { 90 htmlBlock.setPreferredWidth(width); 91 } 92 } 93 94 99 public BoundableRenderable getBlockRenderable() { 100 HtmlBlockPanel htmlBlock = this.htmlBlock; 101 return htmlBlock == null ? null : htmlBlock.getRootRenderable(); 102 } 103 104 112 public FrameSetPanel getFrameSetPanel() { 113 int componentCount = this.getComponentCount(); 114 if(componentCount == 0) { 115 return null; 116 } 117 Object c = this.getComponent(0); 118 if(c instanceof FrameSetPanel) { 119 return (FrameSetPanel) c; 120 } 121 return null; 122 } 123 124 private void setUpAsBlock(UserAgentContext ucontext, HtmlRendererContext rcontext) { 125 HtmlBlockPanel shp = this.createHtmlBlockPanel(ucontext, rcontext); 126 shp.setPreferredWidth(this.preferredWidth); 127 this.htmlBlock = shp; 128 this.frameSetPanel = null; 129 shp.setDefaultPaddingInsets(new Insets(8, 8, 8, 8)); 130 this.removeAll(); 131 this.add(shp); 132 this.nodeRenderer = shp; 133 } 134 135 private void setUpFrameSet(NodeImpl fsrn) { 136 this.isFrameSet = true; 137 this.htmlBlock = null; 138 FrameSetPanel fsp = this.createFrameSetPanel(); 139 this.frameSetPanel = fsp; 140 this.nodeRenderer = fsp; 141 this.removeAll(); 142 this.add(fsp); 143 fsp.setRootNode(fsrn); 144 } 145 146 150 protected HtmlBlockPanel createHtmlBlockPanel(UserAgentContext ucontext, HtmlRendererContext rcontext) { 151 return new HtmlBlockPanel(0, java.awt.Color.WHITE, true, ucontext, rcontext, this); 152 } 153 154 158 protected FrameSetPanel createFrameSetPanel() { 159 return new FrameSetPanel(); 160 } 161 162 168 public void scroll(int x, int y) { 169 } 171 172 178 public void clearDocument() { 179 if(java.awt.EventQueue.isDispatchThread()) { 180 this.clearDocumentImpl(); 181 } 182 else { 183 java.awt.EventQueue.invokeLater(new Runnable () { 184 public void run() { 185 HtmlPanel.this.clearDocumentImpl(); 186 } 187 }); 188 } 189 } 190 191 private void clearDocumentImpl() { 192 HTMLDocumentImpl prevDocument = (HTMLDocumentImpl) this.rootNode; 193 if(prevDocument != null) { 194 prevDocument.removeDocumentNotificationListener(this.notificationListener); 195 } 196 NodeRenderer nr = this.nodeRenderer; 197 if(nr != null) { 198 nr.setRootNode(null); 199 } 200 this.rootNode = null; 201 this.htmlBlock = null; 202 this.nodeRenderer = null; 203 this.isFrameSet = false; 204 this.removeAll(); 205 this.revalidate(); 206 this.repaint(); 207 } 208 209 221 public void setDocument(final Document node, final HtmlRendererContext rcontext, final HtmlParserContext pcontext) { 222 this.setDocument(node, rcontext); 223 } 224 225 240 public void setDocument(final Document node, final HtmlRendererContext rcontext) { 241 if(java.awt.EventQueue.isDispatchThread()) { 242 this.setDocumentImpl(node, rcontext); 243 } 244 else { 245 java.awt.EventQueue.invokeLater(new Runnable () { 246 public void run() { 247 HtmlPanel.this.setDocumentImpl(node, rcontext); 248 } 249 }); 250 } 251 } 252 253 private void setDocumentImpl(Document node, HtmlRendererContext rcontext) { 254 if(!(node instanceof HTMLDocumentImpl)) { 256 throw new IllegalArgumentException ("Only nodes of type HTMLDocumentImpl are currently supported. Use DocumentBuilderImpl."); 257 } 258 HTMLDocumentImpl prevDocument = (HTMLDocumentImpl) this.rootNode; 259 if(prevDocument != null) { 260 prevDocument.removeDocumentNotificationListener(this.notificationListener); 261 } 262 HTMLDocumentImpl nodeImpl = (HTMLDocumentImpl) node; 263 nodeImpl.addDocumentNotificationListener(this.notificationListener); 264 this.rootNode = nodeImpl; 265 NodeImpl fsrn = this.getFrameSetRootNode(nodeImpl); 266 boolean newIfs = fsrn != null; 267 if(newIfs != this.isFrameSet || this.getComponentCount() == 0) { 268 this.isFrameSet = newIfs; 269 if(newIfs) { 270 this.setUpFrameSet(fsrn); 271 } 272 else { 273 this.setUpAsBlock(rcontext.getUserAgentContext(), rcontext); 274 } 275 } 276 NodeRenderer nr = this.nodeRenderer; 277 if(nr != null) { 278 if(newIfs) { 281 nr.setRootNode(fsrn); 282 } 283 else { 284 nr.setRootNode(nodeImpl); 285 } 286 } 287 else { 288 this.invalidate(); 289 this.validate(); 290 this.repaint(); 291 } 292 } 293 294 297 public void setHtml(String htmlSource, String uri, HtmlRendererContext rcontext) { 298 try { 299 DocumentBuilderImpl builder = new DocumentBuilderImpl(rcontext.getUserAgentContext(), rcontext); 300 Reader reader = new StringReader(htmlSource); 301 try { 302 InputSourceImpl is = new InputSourceImpl(reader, uri); 303 Document document = builder.parse(is); 304 this.setDocument(document, rcontext); 305 } finally { 306 reader.close(); 307 } 308 } catch(java.io.IOException ioe) { 309 throw new IllegalStateException ("Unexpected condition.", ioe); 310 } catch(org.xml.sax.SAXException se) { 311 throw new IllegalStateException ("Unexpected condition.", se); 312 } 313 } 314 315 318 public NodeImpl getRootNode() { 319 return this.rootNode; 320 } 321 322 private boolean resetIfFrameSet() { 323 NodeImpl nodeImpl = this.rootNode; 324 NodeImpl fsrn = this.getFrameSetRootNode(nodeImpl); 325 boolean newIfs = fsrn != null; 326 if(newIfs != this.isFrameSet || this.getComponentCount() == 0) { 327 this.isFrameSet = newIfs; 328 if(newIfs) { 329 this.setUpFrameSet(fsrn); 330 NodeRenderer nr = this.nodeRenderer; 331 nr.setRootNode(fsrn); 332 this.validate(); 334 this.repaint(); 335 return true; 336 } 337 } 338 return false; 339 } 340 341 private NodeImpl getFrameSetRootNode(NodeImpl node) { 342 if(node instanceof Document ) { 343 ElementImpl element = (ElementImpl) ((Document ) node).getDocumentElement(); 344 if(element != null && "HTML".equalsIgnoreCase(element.getTagName())) { 345 return this.getFrameSet(element); 346 } 347 else { 348 return this.getFrameSet(node); 349 } 350 } 351 else { 352 return null; 353 } 354 } 355 356 private NodeImpl getFrameSet(NodeImpl node) { 357 NodeImpl[] children = node.getChildrenArray(); 358 if(children == null) { 359 return null; 360 } 361 int length = children.length; 362 NodeImpl frameSet = null; 363 for(int i = 0; i < length; i++) { 364 NodeImpl child = children[i]; 365 if(child instanceof Text ) { 366 } 368 else if(child instanceof ElementImpl) { 369 String tagName = child.getNodeName(); 370 if("HEAD".equalsIgnoreCase(tagName) || 371 "NOFRAMES".equalsIgnoreCase(tagName) || 372 "TITLE".equalsIgnoreCase(tagName) || 373 "META".equalsIgnoreCase(tagName) || 374 "SCRIPT".equalsIgnoreCase(tagName) || 375 "NOSCRIPT".equalsIgnoreCase(tagName)) { 376 } 378 else if("FRAMESET".equalsIgnoreCase(tagName)) { 379 frameSet = child; 380 break; 381 } 382 else { 383 if(this.hasSomeHtml((ElementImpl) child)) { 384 return null; 385 } 386 } 387 } 388 } 389 return frameSet; 390 } 391 392 private boolean hasSomeHtml(ElementImpl element) { 393 String tagName = element.getTagName(); 394 if("HEAD".equalsIgnoreCase(tagName) || "TITLE".equalsIgnoreCase(tagName) || "META".equalsIgnoreCase(tagName)) { 395 return false; 396 } 397 NodeImpl[] children = element.getChildrenArray(); 398 if(children != null) { 399 int length = children.length; 400 for(int i = 0; i < length; i++) { 401 NodeImpl child = children[i]; 402 if(child instanceof Text ) { 403 String textContent = ((Text ) child).getTextContent(); 404 if(textContent != null && !"".equals(textContent.trim())) { 405 return false; 406 } 407 } 408 else if(child instanceof ElementImpl) { 409 if(this.hasSomeHtml((ElementImpl) child)) { 410 return false; 411 } 412 } 413 } 414 } 415 return true; 416 } 417 418 423 public void expandSelection(RenderableSpot rpoint) { 424 HtmlBlockPanel block = this.htmlBlock; 425 if(block != null) { 426 block.setSelectionEnd(rpoint); 427 block.repaint(); 428 this.selectionDispatch.fireEvent(new SelectionChangeEvent(this, block.isSelectionAvailable())); 429 } 430 } 431 432 439 public void resetSelection(RenderableSpot rpoint) { 440 HtmlBlockPanel block = this.htmlBlock; 441 if(block != null) { 442 block.setSelectionStart(rpoint); 443 block.setSelectionEnd(rpoint); 444 block.repaint(); 445 } 446 this.selectionDispatch.fireEvent(new SelectionChangeEvent(this, false)); 447 } 448 449 454 public String getSelectionText() { 455 HtmlBlockPanel block = this.htmlBlock; 456 if(block == null) { 457 return null; 458 } 459 else { 460 return block.getSelectionText(); 461 } 462 } 463 464 472 public org.w3c.dom.Node getSelectionNode() { 473 HtmlBlockPanel block = this.htmlBlock; 474 if(block == null) { 475 return null; 476 } 477 else { 478 return block.getSelectionNode(); 479 } 480 } 481 482 486 public boolean hasSelection() { 487 HtmlBlockPanel block = this.htmlBlock; 488 if(block == null) { 489 return false; 490 } 491 else { 492 return block.hasSelection(); 493 } 494 } 495 496 500 public boolean copy() { 501 HtmlBlockPanel block = this.htmlBlock; 502 if(block != null) { 503 return block.copy(); 504 } 505 else { 506 return false; 507 } 508 } 509 510 515 public void addSelectionChangeListener(SelectionChangeListener listener) { 516 this.selectionDispatch.addListener(listener); 517 } 518 519 523 public void removeSelectionChangeListener(SelectionChangeListener listener) { 524 this.selectionDispatch.removeListener(listener); 525 } 526 527 private ArrayList notifications = new ArrayList(1); 528 529 private void addNotification(DocumentNotification notification) { 530 ArrayList notifs = this.notifications; 532 synchronized(notifs) { 533 notifs.add(notification); 534 } 535 if(EventQueue.isDispatchThread()) { 536 this.processNotifications(); 538 } 539 else { 540 this.notificationTimer.restart(); 541 } 542 } 543 544 549 public void delayedRelayout(NodeImpl node) { 550 ArrayList notifs = this.notifications; 551 synchronized(notifs) { 552 notifs.add(new DocumentNotification(DocumentNotification.SIZE, node)); 553 } 554 this.notificationTimer.restart(); 555 } 556 557 private void processNotifications() { 558 ArrayList notifs = this.notifications; 560 DocumentNotification[] notifsArray; 561 synchronized(notifs) { 562 int size = notifs.size(); 563 if(size == 0) { 564 return; 565 } 566 notifsArray = new DocumentNotification[size]; 567 notifsArray = (DocumentNotification[]) notifs.toArray(notifsArray); 568 notifs.clear(); 569 } 570 int length = notifsArray.length; 571 for(int i = 0; i < length; i++) { 572 DocumentNotification dn = notifsArray[i]; 573 if(dn.node instanceof HTMLFrameSetElement && this.htmlBlock != null) { 574 if(this.resetIfFrameSet()) { 575 return; 577 } 578 } 579 } 580 HtmlBlockPanel blockPanel = this.htmlBlock; 581 if(blockPanel != null) { 582 blockPanel.processDocumentNotifications(notifsArray); 583 } 584 FrameSetPanel frameSetPanel = this.frameSetPanel; 585 if(frameSetPanel != null) { 586 frameSetPanel.processDocumentNotifications(notifsArray); 587 } 588 } 589 590 private class SelectionDispatch extends EventDispatch2 { 591 594 protected void dispatchEvent(EventListener listener, EventObject event) { 595 ((SelectionChangeListener) listener).selectionChanged((SelectionChangeEvent) event); 596 } 597 } 598 599 private class LocalDocumentNotificationListener implements DocumentNotificationListener { 600 public void allInvalidated() { 601 HtmlPanel.this.addNotification(new DocumentNotification(DocumentNotification.GENERIC, null)); 602 } 603 604 public void invalidated(NodeImpl node) { 605 HtmlPanel.this.addNotification(new DocumentNotification(DocumentNotification.GENERIC, node)); 606 } 607 608 public void lookInvalidated(NodeImpl node) { 609 HtmlPanel.this.addNotification(new DocumentNotification(DocumentNotification.LOOK, node)); 610 } 611 612 public void positionInvalidated(NodeImpl node) { 613 HtmlPanel.this.addNotification(new DocumentNotification(DocumentNotification.POSITION, node)); 614 } 615 616 public void sizeInvalidated(NodeImpl node) { 617 HtmlPanel.this.addNotification(new DocumentNotification(DocumentNotification.SIZE, node)); 618 } 619 620 public void externalScriptLoading(NodeImpl node) { 621 } 623 624 public void nodeLoaded(NodeImpl node) { 625 HtmlPanel.this.addNotification(new DocumentNotification(DocumentNotification.GENERIC, node)); 626 } 627 } 628 629 private class NotificationTimerAction implements java.awt.event.ActionListener { 630 public void actionPerformed(ActionEvent e) { 631 HtmlPanel.this.processNotifications(); 632 } 633 } 634 } 635 | Popular Tags |