KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > awt > HtmlRendererImpl


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 package org.openide.awt;
20
21 import org.openide.awt.HtmlLabelUI;
22 import org.openide.awt.HtmlRenderer;
23
24 import java.awt.*;
25 import java.awt.event.*;
26
27 import java.beans.PropertyChangeListener JavaDoc;
28 import java.beans.VetoableChangeListener JavaDoc;
29
30 import java.io.*;
31
32 import java.lang.ref.Reference JavaDoc;
33 import java.lang.ref.SoftReference JavaDoc;
34
35 import javax.swing.*;
36 import javax.swing.border.Border JavaDoc;
37 import javax.swing.event.AncestorListener JavaDoc;
38
39
40 /**
41  * Html renderer component implementation. The actual painting is done by HtmlLabelUI, which uses
42  * HtmlRenderer.renderString(). What this class does: Provide some methods for resetting its state
43  * between uses (see HtmlRenderer.createLabel() for why), overrides for a bunch of things for performance
44  * reasons, and some conversions to handle the case that the lightweight html renderer is disabled
45  * (-J-Dnb.useSwingHtmlRendering=true), to convert our minor extensions to html syntax to standard
46  * syntax for the swing renderer.
47  * <p>
48  * Mainly this class provides an implementation of the various cell renderer interfaces which
49  * HtmlRenderer.Renderer aggregates, and the convenience methods it provides.
50  *
51  * @author Tim Boudreau
52  * @since 4.30
53  *
54  */

55 class HtmlRendererImpl extends JLabel implements HtmlRenderer.Renderer {
56     private static final Rectangle bounds = new Rectangle();
57     private static final boolean swingRendering = Boolean.getBoolean("nb.useSwingHtmlRendering"); //NOI18N
58
private static final Insets EMPTY_INSETS = new Insets(0, 0, 0, 0);
59     static final int TYPE_UNKNOWN = -1;
60     static final int TYPE_TREE = 0;
61     static final int TYPE_LIST = 1;
62     static final int TYPE_TABLE = 2;
63
64     //For experimentation - holding the graphics object may be the source of some
65
//strange painting problems on Apple
66
private static boolean noCacheGraphics = Boolean.getBoolean("nb.renderer.nocache"); //NOI18N
67
private static Reference JavaDoc<Graphics> scratchGraphics = null;
68     private boolean centered = false;
69     private boolean parentFocused = false;
70     private Boolean JavaDoc html = null;
71     private int indent = 0;
72     private Border JavaDoc border = null;
73     private boolean selected = false;
74     private boolean leadSelection = false;
75     private Dimension prefSize = null;
76     private int type = TYPE_UNKNOWN;
77     private int renderStyle = HtmlRenderer.STYLE_CLIP;
78     private boolean enabled = true;
79
80     /** Restore the renderer to a pristine state */
81     public void reset() {
82         parentFocused = false;
83         setCentered(false);
84         html = null;
85         indent = 0;
86         border = null;
87         setIcon(null);
88         setOpaque(false);
89         selected = false;
90         leadSelection = false;
91         prefSize = null;
92         type = TYPE_UNKNOWN;
93         renderStyle = HtmlRenderer.STYLE_CLIP;
94         setFont(UIManager.getFont("controlFont")); //NOI18N
95
setIconTextGap(3);
96         setEnabled(true);
97         border = null;
98
99         //Defensively ensure the insets haven't been messed with
100
EMPTY_INSETS.top = 0;
101         EMPTY_INSETS.left = 0;
102         EMPTY_INSETS.right = 0;
103         EMPTY_INSETS.bottom = 0;
104     }
105
106     public Component getTableCellRendererComponent(
107         JTable table, Object JavaDoc value, boolean selected, boolean leadSelection, int row, int column
108     ) {
109         reset();
110         configureFrom(value, table, selected, leadSelection);
111         type = TYPE_TABLE;
112
113         if (swingRendering && selected) {
114             setBackground(table.getSelectionBackground());
115             setForeground(table.getSelectionForeground());
116             setOpaque(true);
117         }
118
119         return this;
120     }
121
122     public Component getTreeCellRendererComponent(
123         JTree tree, Object JavaDoc value, boolean selected, boolean expanded, boolean leaf, int row, boolean leadSelection
124     ) {
125         reset();
126         configureFrom(value, tree, selected, leadSelection);
127         type = TYPE_TREE;
128
129         if (swingRendering && selected) {
130             if (HtmlLabelUI.isGTK()) {
131                 setBackground(HtmlLabelUI.getBackgroundFor(this));
132                 setForeground(HtmlLabelUI.getForegroundFor(this));
133             }
134             setOpaque(true);
135         }
136
137         return this;
138     }
139
140     public Component getListCellRendererComponent(
141         JList list, Object JavaDoc value, int index, boolean selected, boolean leadSelection
142     ) {
143         reset();
144         configureFrom(value, list, selected, leadSelection);
145         type = TYPE_LIST;
146
147         if (swingRendering && selected) {
148             setBackground(list.getSelectionBackground());
149             setForeground(list.getSelectionForeground());
150             setOpaque(true);
151         }
152         
153         // ##93658: In GTK we have to paint borders in combo boxes
154
if (HtmlLabelUI.isGTK()) {
155             if (index == -1) {
156                 Color borderC = UIManager.getColor("controlShadow");
157                 borderC = borderC == null ? Color.GRAY : borderC;
158                 setBorder(BorderFactory.createCompoundBorder(
159                         BorderFactory.createLineBorder(borderC),
160                         BorderFactory.createEmptyBorder(3, 2, 3, 2)));
161             } else {
162                 setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
163             }
164         }
165
166         return this;
167     }
168
169     /** Generic code to set properties appropriately from any of the renderer
170      * fetching methods */

171     private void configureFrom(Object JavaDoc value, JComponent target, boolean selected, boolean leadSelection) {
172         if (value == null) {
173             value = "";
174         }
175
176         setText((value == null) ? "" : value.toString());
177
178         setSelected(selected);
179
180         if (selected) {
181             setParentFocused(checkFocused(target));
182         } else {
183             setParentFocused(false);
184         }
185
186         setEnabled(target.isEnabled());
187
188         setLeadSelection(leadSelection);
189
190         setFont(target.getFont());
191     }
192
193     private boolean checkFocused(JComponent c) {
194         Component focused = KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner();
195         boolean result = c == focused;
196
197         if (!result) {
198             result = c.isAncestorOf(focused);
199         }
200
201         return result;
202     }
203
204     public void addNotify() {
205         if (swingRendering) {
206             super.addNotify();
207         }
208     }
209
210     public void removeNotify() {
211         if (swingRendering) {
212             super.removeNotify();
213         }
214     }
215
216     public void setSelected(boolean val) {
217         selected = val;
218     }
219
220     public void setParentFocused(boolean val) {
221         parentFocused = val;
222     }
223
224     public void setLeadSelection(boolean val) {
225         leadSelection = val;
226     }
227
228     public void setCentered(boolean val) {
229         centered = val;
230
231         if (val) {
232             setIconTextGap(5);
233         }
234
235         if (swingRendering) {
236             if (val) {
237                 setVerticalTextPosition(JLabel.BOTTOM);
238                 setHorizontalAlignment(JLabel.CENTER);
239                 setHorizontalTextPosition(JLabel.CENTER);
240             } else {
241                 setVerticalTextPosition(JLabel.CENTER);
242                 setHorizontalAlignment(JLabel.LEADING);
243                 setHorizontalTextPosition(JLabel.TRAILING);
244             }
245         }
246     }
247
248     public void setIndent(int pixels) {
249         this.indent = pixels;
250     }
251
252     public void setHtml(boolean val) {
253         Boolean JavaDoc wasHtml = html;
254         String JavaDoc txt = getText();
255         html = val ? Boolean.TRUE : Boolean.FALSE;
256
257         if (swingRendering && (html != wasHtml)) {
258             //Ensure label UI gets updated and builds its little document tree...
259
firePropertyChange("text", txt, getText()); //NOI18N
260
}
261     }
262
263     public void setRenderStyle(int style) {
264         renderStyle = style;
265     }
266
267     int getRenderStyle() {
268         return renderStyle;
269     }
270
271     boolean isLeadSelection() {
272         return leadSelection;
273     }
274
275     boolean isCentered() {
276         return centered;
277     }
278
279     boolean isParentFocused() {
280         return parentFocused;
281     }
282
283     boolean isHtml() {
284         if (html == null) {
285             String JavaDoc s = getText();
286             html = checkHtml(s);
287         }
288
289         return html.booleanValue();
290     }
291
292     private Boolean JavaDoc checkHtml(String JavaDoc s) {
293         Boolean JavaDoc result;
294
295         if (s == null) {
296             result = Boolean.FALSE;
297         } else if (s.startsWith("<html") || s.startsWith("<HTML")) { //NOI18N
298
result = Boolean.TRUE;
299         } else {
300             result = Boolean.FALSE;
301         }
302
303         return result;
304     }
305
306     boolean isSelected() {
307         return selected;
308     }
309
310     int getIndent() {
311         return indent;
312     }
313
314     int getType() {
315         return type;
316     }
317
318     public Dimension getPreferredSize() {
319         if (!swingRendering) {
320             if (prefSize == null) {
321                 prefSize = getUI().getPreferredSize(this);
322             }
323
324             return prefSize;
325         } else {
326             return super.getPreferredSize();
327         }
328     }
329
330     /**
331      * Overridden for the case that we're running with the lightweight html renderer disabled, to convert
332      * any less-than-legal html to legal html for purposes of Swing's html rendering.
333      *
334      * @return The text - unless the renderer is disabled, this just return super.getText()
335      */

336     public String JavaDoc getText() {
337         String JavaDoc result = super.getText();
338
339         if (swingRendering && Boolean.TRUE.equals(html)) {
340             //Standard swing rendering needs an opening HTML tag to function, so make sure there is
341
//one if we're not using HtmlLabelUI
342
result = ensureHtmlTags(result);
343         } else if (swingRendering && (html == null)) {
344             //Cannot call isHtml() here, it will create an endless loop, so manually check the HTML status
345
html = checkHtml(super.getText());
346
347             if (Boolean.TRUE.equals(html)) {
348                 result = ensureHtmlTags(result);
349             }
350         }
351
352         return result;
353     }
354
355     /**
356      * Converts our extended html syntax (allowing UIManager color keys and omitting opening html tags
357      * into standard html. Only called if the lightweight html renderer is disabled and we're running with
358      * a standard JLabel UI
359      *
360      * @param s The string that is the text of the label
361      * @return The same string converted to standard HTML Swing's rendering infrastructure will know what to do
362      * with
363      */

364     private String JavaDoc ensureHtmlTags(String JavaDoc s) {
365         s = ensureLegalFontColorTags(s);
366
367         if (!s.startsWith("<HTML") && !s.startsWith("<html")) { //NOI18N
368
s = "<html>" + s + "</html>"; //NOI18N
369
}
370
371         return s;
372     }
373
374     /**
375      * Converts extended UI manager color tags into legal html in the case that we're using swing rendering
376      *
377      * @param s string to convert if it has questionable font tags
378      * @return The converted string
379      */

380     private static String JavaDoc ensureLegalFontColorTags(String JavaDoc s) {
381         String JavaDoc check = s.toUpperCase();
382         int start = 0;
383         int fidx = check.indexOf("<FONT", start); //NOI18N
384
StringBuffer JavaDoc sb = null;
385
386         if ((fidx != -1) && (fidx <= s.length())) {
387             while ((fidx != -1) && (fidx <= s.length())) {
388                 int cidx = check.indexOf("COLOR", start); //NOI18N
389
int tagEnd = check.indexOf('>', start); //NOI18N
390
start = tagEnd + 1;
391
392                 if (tagEnd == -1) {
393                     break;
394                 }
395
396                 if (cidx != -1) {
397                     if (cidx < tagEnd) {
398                         //we have a font color tag
399
int eidx = check.indexOf('=', cidx); //NOI18N
400

401                         if (eidx != -1) {
402                             int bangIdx = check.indexOf('!', eidx); //NOI18N
403

404                             if ((bangIdx != -1) && (bangIdx < tagEnd)) {
405                                 int colorStart = bangIdx + 1;
406                                 int colorEnd = tagEnd;
407
408                                 for (int i = colorStart; i < tagEnd; i++) {
409                                     char c = s.charAt(i);
410
411                                     if (!Character.isLetter(c)) {
412                                         colorEnd = i;
413
414                                         break;
415                                     }
416                                 }
417
418                                 if (sb == null) {
419                                     sb = new StringBuffer JavaDoc(s);
420                                 }
421
422                                 String JavaDoc colorString = s.substring(colorStart, colorEnd);
423                                 String JavaDoc converted = convertToStandardColor(colorString);
424                                 sb.replace(bangIdx, colorEnd, converted);
425                                 s = sb.toString();
426                                 check = s.toUpperCase();
427                             }
428                         }
429                     }
430                 }
431
432                 fidx = check.indexOf("<FONT", start); //NOI18N
433
start = fidx;
434             }
435         }
436
437         if (sb != null) {
438             return sb.toString();
439         } else {
440             return s;
441         }
442     }
443
444     /**
445      * Creates a standard html #nnnnnn string from a string representing a UIManager key. If the color is not found,
446      * black will be used. Only used if the lightweight html renderer is disabled.
447      *
448      * @param colorString A string found after a ! character in a color definition, which needs to be converted to
449      * standard HTML
450      * @return A hex number string
451      */

452     private static String JavaDoc convertToStandardColor(String JavaDoc colorString) {
453         Color c = UIManager.getColor(colorString);
454
455         if (c == null) {
456             c = Color.BLACK;
457         }
458
459         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(7);
460         sb.append('#');
461         sb.append(hexString(c.getRed()));
462         sb.append(hexString(c.getGreen()));
463         sb.append(hexString(c.getBlue()));
464
465         return sb.toString();
466     }
467
468     /**
469      * Gets a hex string for an integer. Ensures the result is always two characters long, which is not
470      * true of Integer.toHexString().
471      *
472      * @param r an integer < 255
473      * @return a 2 character hexadecimal string
474      */

475     private static String JavaDoc hexString(int r) {
476         String JavaDoc s = Integer.toHexString(r);
477
478         if (s.length() == 1) {
479             s = '0' + s;
480         }
481
482         return s;
483     }
484
485     /** Overridden to do nothing under normal circumstances. If the boolean flag to <strong>not</strong> use the
486      * internal HTML renderer is in effect, this will fire changes normally */

487     protected final void firePropertyChange(String JavaDoc name, Object JavaDoc old, Object JavaDoc nue) {
488         if (swingRendering) {
489             if ("text".equals(name) && isHtml()) {
490                 //Force in the HTML tags so the UI will set up swing HTML rendering appropriately
491
nue = getText();
492             }
493
494             super.firePropertyChange(name, old, nue);
495         }
496     }
497
498     public Border JavaDoc getBorder() {
499         Border JavaDoc result;
500
501         if ((indent != 0) && swingRendering) {
502             result = BorderFactory.createEmptyBorder(0, indent, 0, 0);
503         } else {
504             result = border;
505         }
506
507         return result;
508     }
509
510     public void setBorder(Border JavaDoc b) {
511         Border JavaDoc old = border;
512         border = b;
513
514         if (swingRendering) {
515             firePropertyChange("border", old, b);
516         }
517     }
518
519     public Insets getInsets() {
520         Insets result;
521
522         //Call getBorder(), not just read the field - if swingRendering, the border will be constructed, and the
523
//insets are what will make the indent property work; HtmlLabelUI doesn't need this, it just reads the
524
//insets property, but BasicLabelUI and its ilk do
525
Border JavaDoc b = getBorder();
526
527         if (b == null) {
528             result = EMPTY_INSETS;
529         } else {
530             result = b.getBorderInsets(this);
531         }
532
533         return result;
534     }
535
536     public void setEnabled(boolean b) {
537         //OptimizeIt shows about 12Ms overhead calling back to Component.enable(), so avoid it if possible
538
enabled = b;
539
540         if (swingRendering) {
541             super.setEnabled(b);
542         }
543     }
544
545     public boolean isEnabled() {
546         return enabled;
547     }
548
549     public void updateUI() {
550         if (swingRendering) {
551             super.updateUI();
552         } else {
553             setUI(HtmlLabelUI.createUI(this));
554         }
555     }
556
557     /** Overridden to produce a graphics object even when isDisplayable() is
558      * false, so that calls to getPreferredSize() will return accurate
559      * dimensions (presuming the font and text are set correctly) even when
560      * not onscreen. */

561     public Graphics getGraphics() {
562         Graphics result = null;
563
564         if (isDisplayable()) {
565             result = super.getGraphics();
566         }
567
568         if (result == null) {
569             result = scratchGraphics();
570         }
571
572         return result;
573     }
574
575     /** Fetch a scratch graphics object for calculating preferred sizes while
576      * offscreen */

577     private static final Graphics scratchGraphics() {
578         Graphics result = null;
579
580         if (scratchGraphics != null) {
581             result = scratchGraphics.get();
582
583             if (result != null) {
584                 result.setClip(null); //just in case somebody did something nasty
585
}
586         }
587
588         if (result == null) {
589             result = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration()
590                                         .createCompatibleImage(1, 1).getGraphics();
591
592             if (!noCacheGraphics) {
593                 scratchGraphics = new SoftReference JavaDoc<Graphics>(result);
594             }
595         }
596
597         return result;
598     }
599
600     public void setBounds(int x, int y, int w, int h) {
601         if (swingRendering) {
602             super.setBounds(x, y, w, h);
603         }
604
605         bounds.setBounds(x, y, w, h);
606     }
607
608     public void reshape(int x, int y, int w, int h) {
609         if (swingRendering) {
610             super.reshape(x, y, w, h);
611         }
612     }
613
614     public int getWidth() {
615         return bounds.width;
616     }
617
618     public int getHeight() {
619         return bounds.height;
620     }
621
622     public Point getLocation() {
623         return bounds.getLocation();
624     }
625
626     /** Overridden to do nothing for performance reasons */
627     public void validate() {
628         //do nothing
629
}
630
631     /** Overridden to do nothing for performance reasons */
632     public void repaint(long tm, int x, int y, int w, int h) {
633         //do nothing
634
}
635
636     /** Overridden to do nothing for performance reasons */
637     public void repaint() {
638         //do nothing
639
}
640
641     /** Overridden to do nothing for performance reasons */
642     public void invalidate() {
643         //do nothing
644
}
645
646     /** Overridden to do nothing for performance reasons */
647     public void revalidate() {
648         //do nothing
649
}
650
651     /** Overridden to do nothing for performance reasons */
652     public void addAncestorListener(AncestorListener JavaDoc l) {
653         if (swingRendering) {
654             super.addAncestorListener(l);
655         }
656     }
657
658     /** Overridden to do nothing for performance reasons */
659     public void addComponentListener(ComponentListener l) {
660         if (swingRendering) {
661             super.addComponentListener(l);
662         }
663     }
664
665     /** Overridden to do nothing for performance reasons */
666     public void addContainerListener(ContainerListener l) {
667         if (swingRendering) {
668             super.addContainerListener(l);
669         }
670     }
671
672     /** Overridden to do nothing for performance reasons */
673     public void addHierarchyListener(HierarchyListener l) {
674         if (swingRendering) {
675             super.addHierarchyListener(l);
676         }
677     }
678
679     /** Overridden to do nothing for performance reasons */
680     public void addHierarchyBoundsListener(HierarchyBoundsListener l) {
681         if (swingRendering) {
682             super.addHierarchyBoundsListener(l);
683         }
684     }
685
686     /** Overridden to do nothing for performance reasons */
687     public void addInputMethodListener(InputMethodListener l) {
688         if (swingRendering) {
689             super.addInputMethodListener(l);
690         }
691     }
692
693     /** Overridden to do nothing for performance reasons */
694     public void addFocusListener(FocusListener fl) {
695         if (swingRendering) {
696             super.addFocusListener(fl);
697         }
698     }
699
700     /** Overridden to do nothing for performance reasons */
701     public void addMouseListener(MouseListener ml) {
702         if (swingRendering) {
703             super.addMouseListener(ml);
704         }
705     }
706
707     /** Overridden to do nothing for performance reasons */
708     public void addMouseWheelListener(MouseWheelListener ml) {
709         if (swingRendering) {
710             super.addMouseWheelListener(ml);
711         }
712     }
713
714     /** Overridden to do nothing for performance reasons */
715     public void addMouseMotionListener(MouseMotionListener ml) {
716         if (swingRendering) {
717             super.addMouseMotionListener(ml);
718         }
719     }
720
721     /** Overridden to do nothing for performance reasons */
722     public void addVetoableChangeListener(VetoableChangeListener JavaDoc vl) {
723         if (swingRendering) {
724             super.addVetoableChangeListener(vl);
725         }
726     }
727
728     /** Overridden to do nothing for performance reasons, unless using standard swing rendering */
729     public void addPropertyChangeListener(String JavaDoc s, PropertyChangeListener JavaDoc l) {
730         if (swingRendering) {
731             super.addPropertyChangeListener(s, l);
732         }
733     }
734
735     public void addPropertyChangeListener(PropertyChangeListener JavaDoc l) {
736         if (swingRendering) {
737             super.addPropertyChangeListener(l);
738         }
739     }
740 }
741
Popular Tags