KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > lobobrowser > html > gui > HtmlBlockPanel


1 /*
2     GNU LESSER GENERAL PUBLIC LICENSE
3     Copyright (C) 2006 The Lobo Project
4
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Lesser General Public
7     License as published by the Free Software Foundation; either
8     version 2.1 of the License, or (at your option) any later version.
9
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13     Lesser General Public License for more details.
14
15     You should have received a copy of the GNU Lesser General Public
16     License along with this library; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19     Contact info: xamjadmin@users.sourceforge.net
20 */

21 /*
22  * Created on Apr 16, 2005
23  */

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 /**
42  * A Swing component that renders a HTML block, given
43  * by a DOM root or an internal element, typically a DIV.
44  * This component cannot render FRAMESETs.
45  * @see HtmlPanel
46  * @see FrameSetPanel
47  * @author J. H. S.
48  */

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 JavaDoc 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             /* (non-Javadoc)
101              * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
102              */

103             public void mouseDragged(MouseEvent e) {
104                 onMouseDragged(e);
105             }
106
107             /* (non-Javadoc)
108              * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
109              */

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     /**
125      * Allows {@link #getPreferredSize()} to render the HTML block
126      * in order to determine the preferred size of this component.
127      * Note that <code>getPreferredSize()<code> is a potentially time-consuming
128      * operation if the preferred width is set.
129      * @param width The preferred blocked width. Use <code>-1</code> to unset.
130      */

131     public void setPreferredWidth(int width) {
132         this.preferredWidth = width;
133     }
134
135     /**
136      * If the preferred size has been set with {@link #setPreferredSize(Dimension)},
137      * then that size is returned. Otherwise a preferred size is calculated by
138      * rendering the HTML DOM, provided one is available and a preferred width other
139      * than <code>-1</code> has been set with {@link #setPreferredWidth(int)}.
140      * An arbitrary preferred size is returned in other scenarios.
141      */

142     public Dimension getPreferredSize() {
143         // Expected to be invoked in the GUI thread.
144
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                 // Potentially does layout outside of GUI thread.
152
// Do not provide default overflow.
153
block.layout(pw, 0, false, false, RBlock.OVERFLOW_NONE);
154                 // Adjust for permanent vertical scrollbar.
155
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 JavaDoc {
163         super.finalize();
164     }
165     
166     public boolean copy() {
167         String JavaDoc 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 JavaDoc 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     /**
215      * Sets the root node to render. This method should
216      * be invoked in the GUI dispatch thread.
217      */

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     /**
246      * @deprecated setContainer is unused.
247      */

248     public void setRootNode(NodeImpl node, boolean setContainer) {
249         this.setRootNode(node);
250     }
251
252     protected void revalidatePanel() {
253         // Called in the GUI thread.
254
this.invalidate();
255         this.validate();
256         //TODO: Could be paintImmediately.
257
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         // Rely on AWT mouse-click only for double-clicks
267
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 // BoundableRenderable r = block.getRenderable(point);
274
// if(r != null) {
275
// Point rpoint = r.getRenderablePoint(point.x, point.y);
276
// r.onDoubleClick(event, rpoint.x, rpoint.y);
277
// }
278
}
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 // BoundableRenderable r = block.getRenderable(point);
302
// if(r != null) {
303
// this.mousePressTarget = r;
304
// Point rpoint = r.getRenderablePoint(point.x, point.y);
305
// int rx = rpoint.x;
306
// int ry = rpoint.y;
307
// r.onMousePressed(event, rx, ry);
308
// RenderableSpot rp = r.getLowestRenderableSpot(rx, ry);
309
// if(rp != null) {
310
// this.frameContext.resetSelection(rp);
311
// }
312
// else {
313
// this.frameContext.resetSelection(null);
314
// }
315
// }
316
// else {
317
// this.frameContext.resetSelection(null);
318
// }
319
}
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                 // TODO: This will be raised twice on a double-click.
330
block.onMouseClick(event, rx, ry);
331             }
332             block.onMouseReleased(event, rx, ry);
333             
334 // BoundableRenderable r = block.getRenderable(point);
335
// if(r != null) {
336
// Point rpoint = r.getRenderablePoint(point.x, point.y);
337
// int rx = rpoint.x;
338
// int ry = rpoint.y;
339
// // First, generate a mouse-click event if it's BUTTON1.
340
// if(event.getButton() == MouseEvent.BUTTON1) {
341
// // TODO: This will be raised twice on a double-click.
342
// r.onMouseClick(event, rx, ry);
343
// }
344
// // Now generate mouse-released event.
345
// r.onMouseReleased(event, rx, ry);
346
// }
347

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     /* (non-Javadoc)
394      * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
395      */

396     //protected void paintComponent(Graphics g) {
397
public void paint(Graphics g) {
398         // We go against Sun's advice and override
399
// paint() instead of paintComponent(). Scrollbars
400
// do not repaint correctly if we use
401
// paintComponent.
402
if(this.isOpaque()) {
403             // Background not painted by default in JComponent.
404
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 JavaDoc uri = rootNode instanceof Document ? ((Document) rootNode).getDocumentURI() : "";
422                 logger.info("paintComponent(): URI=[" + uri + "]. Block paint elapsed: " + (time2 - time1) + " ms.");
423             }
424
425             // Paint FrameContext selection
426

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                 //Only set origin
450
//this.rblock.setBounds(0, 0, size.width, size.height);
451
block.setOrigin(0, 0);
452                 block.updateWidgetBounds(0, 0);
453                 if(liflag) {
454                     long time2 = System.currentTimeMillis();
455                     String JavaDoc 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 JavaDoc() {
462                 public void run() {
463                     HtmlBlockPanel.this.doLayout();
464                 }
465             });
466         }
467     }
468     
469     /**
470      * Implementation of UINode.repaint().
471      */

472     public void repaint(ModelNode modelNode) {
473         //this.rblock.invalidateRenderStyle();
474
this.repaint();
475     }
476
477     public String JavaDoc getSelectionText() {
478         RenderableSpot start = this.startSelection;
479         RenderableSpot end = this.endSelection;
480         if(start != null && end != null) {
481             StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
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         // Overridding with NOP. For various reasons,
503
// the regular mechanism for painting children
504
// needs to be handled by Cobra.
505
}
506     
507     public Color getPaintedBackgroundColor() {
508         return this.isOpaque() ? this.getBackground() : null;
509     }
510
511     /* (non-Javadoc)
512      * @see java.awt.datatransfer.ClipboardOwner#lostOwnership(java.awt.datatransfer.Clipboard, java.awt.datatransfer.Transferable)
513      */

514     public void lostOwnership(Clipboard arg0, Transferable arg1) {
515     }
516
517     public void relayout() {
518         // Expected to be called in the GUI thread.
519
// Renderable branch should be invalidated at this
520
// point, but this GUI component not necessarily.
521
this.revalidatePanel();
522     }
523     
524     public void invalidateLayoutUpTree() {
525         // Called when renderable branch is invalidated.
526
// We shouldn't do anything here. Changes in renderer
527
// tree do not have any bearing on validity of GUI
528
// component.
529
}
530
531     public void updateAllWidgetBounds() {
532         this.rblock.updateWidgetBounds(0, 0);
533     }
534
535     public Point getGUIPoint(int clientX, int clientY) {
536         // This is the GUI!
537
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         // Called in the GUI thread.
548
if(this.processingDocumentNotification) {
549             // This should not be possible. Even if
550
// Javascript modifies the DOM during
551
// parsing, this should be executed in
552
// the GUI thread, not the parser thread.
553
throw new IllegalStateException JavaDoc("Recursive");
554         }
555         this.processingDocumentNotification = true;
556         try {
557             //Note: It may be assumed that usually only generic
558
//notifications come in batches. Other types
559
//of noitifications probably come one by one.
560
boolean topLayout = false;
561             java.util.ArrayList JavaDoc 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                         // This is all-invalidate (new style sheet)
572
if(loggableInfo) {
573                             logger.info("processDocumentNotifications(): Calling invalidateLayoutDeep().");
574                         }
575                         this.rblock.invalidateLayoutDeep();
576                         //this.rblock.invalidateRenderStyle();
577
}
578                     else {
579                         UINode uiNode = node.findUINode();
580                         if(uiNode != null) {
581                             RElement relement = (RElement) uiNode;
582                             relement.invalidateLayoutUpTree();
583 // if(type == DocumentNotification.GENERIC) {
584
// relement.invalidateRenderStyle();
585
// }
586
}
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                     //TODO: Could be more efficient.
598
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                         //relement.invalidateRenderStyle();
619
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         // NOP
646
}
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