KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > explorer > propertysheet > ButtonPanel


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 /*
20  * ButtonPanel.java
21  *
22  * Created on December 15, 2002, 5:45 PM
23  */

24 package org.openide.explorer.propertysheet;
25
26 import java.awt.*;
27 import java.awt.event.FocusListener JavaDoc;
28 import java.awt.geom.AffineTransform JavaDoc;
29 import java.awt.image.BufferedImage JavaDoc;
30
31 import javax.swing.*;
32
33
34 /** This class acts as a container for property table cell
35  * editors that support custom editors, and as a cell
36  * renderer proxy that will display the custom editor button.
37  * This ensures that renderers appear identical
38  * to editors, and that any changes to the appearance of the
39  * button that launches the custom editor are made, they will
40  * appear automatically in both renderers and editors.
41  * <p>
42  * It implements InplaceEditor to proxy the real inplace editor it contains wraps,
43  * and desplays the component returned by InplaceEditor.getComponent on the
44  * inplace editor it is currently wrapping.
45  *
46  * @author Tim Boudreau
47  */

48 class ButtonPanel extends javax.swing.JComponent JavaDoc implements InplaceEditor {
49     public static final Object JavaDoc editorActionKey = "openCustomEditor"; //NOI18N
50

51     /** Store this value since we will check it for every paint or layout */
52     private final boolean log = PropUtils.isLoggable(ButtonPanel.class);
53
54     /** The component to be rendered in the left side of the component or the
55      *full component in the case the custom editor button should not be displayed. */

56     JComponent comp = null;
57     private ConditionallyFocusableButton button;
58     boolean needLayout = true;
59     private InplaceEditor inplace = null;
60     boolean clearing = false;
61
62     /** Creates a new instance of ButtonPanel */
63     public ButtonPanel() {
64         createButton();
65         setOpaque(true);
66     }
67
68     private void createButton() {
69         button = new ConditionallyFocusableButton();
70
71         int buttonWidth = PropUtils.getCustomButtonWidth();
72         button.setBounds(getWidth() - buttonWidth, 0, buttonWidth, getHeight());
73         button.setIcon(PropUtils.getCustomButtonIcon());
74         button.setRolloverIcon(PropUtils.getCustomButtonIcon());
75         button.setMargin(null);
76         button.setName("Custom editor button - editor instance"); //NOI18N
77
button.setText(null);
78
79         //undocumented (?) call to hide action text - see JButton line 234
80
button.putClientProperty("hideActionText", Boolean.TRUE); //NOI18N
81

82         //Don't allow button to receive focus - otherwise it can when it's
83
//removed
84
// button.setFocusable(false);
85
add(button);
86
87         //setFocusable(false);
88
}
89
90     void setButtonAction(Action a) {
91         button.setAction(a);
92         button.setIcon(PropUtils.getCustomButtonIcon());
93         button.setRolloverIcon(PropUtils.getCustomButtonIcon());
94     }
95
96     public void setOpaque(boolean b) {
97         if (getInplaceEditor() != null) {
98             getInplaceEditor().getComponent().setOpaque(true);
99         }
100     }
101
102     public void setFont(Font f) {
103         if (comp != null) {
104             comp.setFont(f);
105         }
106
107         super.setFont(f);
108     }
109
110     public InplaceEditor getInplaceEditor() {
111         return inplace;
112     }
113
114     public void setCustomButtonBackground(Color c) {
115         button.setBackground(c);
116     }
117
118     public void setRolloverPoint(Point p) {
119         if (p != null) {
120             if (p.x < (getWidth() - PropUtils.getCustomButtonWidth())) {
121                 button.getModel().setRollover(false);
122
123                 if (comp instanceof AbstractButton) {
124                     ((AbstractButton) comp).getModel().setRollover(true);
125                 }
126             } else {
127                 button.getModel().setRollover(true);
128
129                 if (comp instanceof AbstractButton) {
130                     ((AbstractButton) comp).getModel().setRollover(false);
131                 }
132             }
133         } else {
134             button.getModel().setRollover(false);
135
136             if (comp instanceof AbstractButton) {
137                 ((AbstractButton) comp).getModel().setRollover(false);
138             }
139         }
140     }
141
142     public Dimension getPreferredSize() {
143         Dimension result;
144
145         if (comp != null) {
146             result = new Dimension(comp.getPreferredSize());
147             result.width += button.getWidth();
148             result.height = Math.max(result.height, button.getPreferredSize().height);
149         } else {
150             result = new Dimension(button.getPreferredSize());
151         }
152
153         return result;
154     }
155
156     /** Overridden to forward the setEnabled call to the contained
157      * component - the custom editor button should always be
158      * enabled if present */

159     public void setEnabled(boolean val) {
160         super.setEnabled(val);
161
162         if (comp != null) {
163             comp.setEnabled(val);
164         }
165
166         button.setEnabled(true);
167     }
168
169     /** Set the component that will render (or be
170      * editor for) the property value. The component
171      * <strong>must</strong> be set <strong>before</strong>
172      * the instance is added to a container (the add will
173      * happen on <code>addNotify()</code>)*/

174     private void setComponent(JComponent c) {
175         if (c == comp) {
176             return;
177         }
178
179         if ((comp != null) && (comp.getParent() == this)) {
180             remove(comp);
181         }
182
183         if (log) {
184             PropUtils.log(ButtonPanel.class, "Button panel setComponent to " + c);
185         }
186
187         comp = c;
188
189         if (comp != null) {
190             comp.setBackground(getBackground());
191             comp.setForeground(getForeground());
192
193             if (comp.isEnabled() != isEnabled()) {
194                 comp.setEnabled(isEnabled());
195             }
196
197             add(comp);
198         }
199
200         needLayout = true;
201     }
202
203     public void setBackground(Color c) {
204         super.setBackground(c);
205
206         if (comp != null) {
207             comp.setBackground(c);
208
209             Color bttn = PropUtils.getButtonColor();
210
211             if (bttn == null) {
212                 button.setBackground(c);
213             } else {
214                 button.setBackground(bttn);
215             }
216         }
217     }
218
219     public void setForeground(Color c) {
220         super.setForeground(c);
221
222         if (comp != null) {
223             comp.setForeground(c);
224
225             if (PropUtils.getButtonColor() == null) {
226                 button.setForeground(c);
227             }
228         }
229     }
230
231     public void paint(Graphics g) {
232         if (isShowing()) {
233             super.paint(g);
234
235             return;
236         }
237
238         if (needLayout) {
239             doLayout();
240         }
241
242         int width = getWidth();
243
244         //We're painting in a PropertyRenderer, no parent present
245
Graphics cg = g.create(0, 0, width - button.getWidth(), getHeight());
246
247         try {
248             if (comp instanceof InplaceEditor) {
249                 comp.paint(cg);
250
251                 if (comp.getParent() != this) {
252                     add(comp);
253                 }
254             }
255         } finally {
256             cg.dispose();
257         }
258
259         cg = g.create(width - button.getWidth(), 0, button.getWidth(), getHeight());
260
261         try {
262             button.paint(cg);
263         } finally {
264             cg.dispose();
265         }
266
267         /** Problem with endless looping in windows look and feel - painting
268          * causes some fiddling with component hierarchy */

269         if (getParent() instanceof CellRendererPane) {
270             RepaintManager.currentManager(this).markCompletelyClean(this);
271         }
272     }
273
274     /** Overridden to flag that a layout needs to be performed. This is
275      * needed since the component may be painted without a parent, so
276      * invalidate will not do anything */

277     @SuppressWarnings JavaDoc("deprecation")
278     public void reshape(int x, int y, int w, int h) {
279         super.reshape(x, y, w, h);
280         needLayout = true;
281     }
282
283     /** Overridden to force focus requests to the contained editor
284      * component - setting focus to this component directly will
285      * never be desirable. */

286     public void requestFocus() {
287         if (comp != null) {
288             comp.requestFocus();
289         }
290     }
291
292     /** Overridden to force focus requests to the contained editor
293      * component - setting focus to this component directly will
294      * never be desirable. */

295     public boolean requestFocusInWindow() {
296         if (comp != null) {
297             return comp.requestFocusInWindow();
298         } else {
299             return false;
300         }
301     }
302
303     /** Overridden to proxy adds to the custom editor button and the
304      * installed component */

305     public void addFocusListener(FocusListener JavaDoc l) {
306         if (comp != null) {
307             button.addFocusListener(l);
308             comp.addFocusListener(l);
309         }
310     }
311
312     /** Overridden to proxy removes to the custom editor button and the
313      * installed component */

314     public void removeFocusListener(FocusListener JavaDoc l) {
315         if (comp != null) {
316             button.removeFocusListener(l);
317             comp.removeFocusListener(l);
318         }
319     }
320
321     public void setInplaceEditor(InplaceEditor ed) {
322         if (inplace == ed) {
323             if (isAncestorOf(inplace.getComponent())) {
324                 return;
325             }
326         }
327
328         if (inplace != null) {
329             setComponent(null);
330         }
331
332         inplace = ed;
333         setComponent(inplace.getComponent());
334         needLayout = true;
335     }
336
337     //*********InplaceEditor impl that proxies to the embedded inplace editor*****
338
public void addActionListener(java.awt.event.ActionListener JavaDoc al) {
339         inplace.addActionListener(al);
340     }
341
342     public void clear() {
343         clearing = true;
344
345         try {
346             inplace.clear();
347             inplace = null;
348             setComponent(null);
349         } finally {
350             clearing = false;
351         }
352     }
353
354     /** Get the component currently assigned as the real editor
355      * embedded in this component. While not strictly necessary,
356      * this is useful if there are issues with focus bugs stemming
357      * from specific component types which need to be handled by
358      * the parent table. */

359     public JComponent getComponent() {
360         return this;
361     }
362
363     public void connect(java.beans.PropertyEditor JavaDoc pe, PropertyEnv env) {
364         inplace.connect(pe, env);
365     }
366
367     public KeyStroke[] getKeyStrokes() {
368         return inplace.getKeyStrokes();
369     }
370
371     public java.beans.PropertyEditor JavaDoc getPropertyEditor() {
372         return inplace.getPropertyEditor();
373     }
374
375     public PropertyModel getPropertyModel() {
376         return inplace.getPropertyModel();
377     }
378
379     public Object JavaDoc getValue() {
380         return inplace.getValue();
381     }
382
383     public boolean isKnownComponent(Component c) {
384         // return c == this || c == button || inplace.isKnownComponent(c);
385
return (c == this) || inplace.isKnownComponent(c);
386     }
387
388     public void removeActionListener(java.awt.event.ActionListener JavaDoc al) {
389         inplace.removeActionListener(al);
390     }
391
392     public void reset() {
393         inplace.reset();
394     }
395
396     public void setPropertyModel(PropertyModel pm) {
397         inplace.setPropertyModel(pm);
398     }
399
400     public void setValue(Object JavaDoc o) {
401         inplace.setValue(o);
402     }
403
404     public boolean supportsTextEntry() {
405         return inplace.supportsTextEntry();
406     }
407
408     public void doLayout() {
409         if (comp != null) {
410             comp.setBounds(0, 0, getWidth() - PropUtils.getCustomButtonWidth(), getHeight());
411             comp.doLayout();
412         }
413
414         button.setBounds(
415             getWidth() - PropUtils.getCustomButtonWidth(), 0, PropUtils.getCustomButtonWidth(), getHeight()
416         );
417
418         if (log) {
419             PropUtils.log(
420                 ButtonPanel.class,
421                 "Laying out button panel. Bounds" + " are " + getBounds() + ", custom editor button bounds: " +
422                 button.getBounds() + " comp is " + comp
423             ); //NOI18N
424
}
425
426         needLayout = false;
427     }
428
429     public Dimension getMinimumSize() {
430         return getPreferredSize();
431     }
432
433     /** This handles the problem that the order of removal is that when
434      * the editor is removed, first the inner component is removed. At
435      * that point, the focus subsystem will try to give focus to the first
436      * focusable sibling of the inner component, which is the custom editor
437      * button, which is still there. However, when the editor is removed,
438      * focus never returns to the table - it stays on the now-offscreen
439      * custom editor button.
440      * <p>
441      * This class also contains the ability to create an image buffer of itself
442      * and use it for its lifetime. On XP and Aqua L&Fs, button painting is
443      * expensive, and a huge amount of a treetable or property sheet's painting
444      * cycle gets spent scaling the backing bitmap for a button that will
445      * always be painted exactly the same size.
446      */

447     private class ConditionallyFocusableButton extends JButton {
448         private AffineTransform JavaDoc at = AffineTransform.getTranslateInstance(0, 0);
449         private BufferedImage JavaDoc snapshot = null;
450
451         public ConditionallyFocusableButton() {
452         }
453
454         public boolean isFocusable() {
455             return (ButtonPanel.this.getParent() != null) && !clearing;
456         }
457
458         public void paint(Graphics g) {
459             if (PropUtils.useOptimizedCustomButtonPainting() && !hasFocus()) {
460                 if (log) {
461                     PropUtils.log(
462                         ButtonPanel.class,
463                         "Blitting custom editor " + "button backing store for button at " + getBounds() + " in " +
464                         ((getParent() == null) ? " null parent" : (getParent() + "editor=" + inplace))
465                     ); //NOI18N
466
}
467
468                 ((Graphics2D) g).drawRenderedImage(getSnapshot(), at);
469             } else {
470                 if (log) {
471                     PropUtils.log(
472                         ButtonPanel.class,
473                         "Painting unoptimized custom editor " + "button button at " + getBounds() + " in " +
474                         ((getParent() == null) ? " null parent" : (getParent() + "editor=" + inplace))
475                     ); //NOI18N
476
}
477
478                 super.paint(g);
479             }
480         }
481
482         public BufferedImage JavaDoc getSnapshot() {
483             if (snapshot == null) {
484                 snapshot = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice()
485                                               .getDefaultConfiguration().createCompatibleImage(getWidth(), getHeight());
486
487                 if (log) {
488                     PropUtils.log(ButtonPanel.class, "Created " + snapshot + " custom editor button backing image");
489                 }
490
491                 if (snapshot.getAlphaRaster() == null) {
492                     //Alpha not supported, could cause corruption (issue
493
//39280) - use a bufferedImage which will support alpha,
494
//although less efficient to blit
495
snapshot = new BufferedImage JavaDoc(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
496                 }
497
498                 Graphics g = snapshot.getGraphics();
499                 super.paint(g);
500             }
501
502             return snapshot;
503         }
504     }
505 }
506
Popular Tags