001package org.jdesktop.swingx.prompt;
002
003import java.awt.Color;
004import java.awt.Font;
005
006import javax.swing.text.JTextComponent;
007
008import org.jdesktop.swingx.JXFormattedTextField;
009import org.jdesktop.swingx.JXTextArea;
010import org.jdesktop.swingx.JXTextField;
011import org.jdesktop.swingx.painter.Painter;
012import org.jdesktop.swingx.painter.Painters;
013import org.jdesktop.swingx.plaf.PromptTextUI;
014import org.jdesktop.swingx.plaf.TextUIWrapper;
015
016/**
017 * <p>
018 * Sets prompt text, foreground, background and {@link FocusBehavior} properties
019 * on a JTextComponent by calling
020 * {@link JTextComponent#putClientProperty(Object, Object)}. These properties
021 * are used by {@link PromptTextUI} instances to render the prompt of a text
022 * component.
023 * </p>
024 * 
025 * <p>
026 * This class is used by {@link JXTextField}, {@link JXFormattedTextField} and
027 * {@link JXTextArea} to get and set prompt properties. {@link PromptTextUI}
028 * retrieves these properties using PromptSupport.
029 * </p>
030 * 
031 * @see JXTextField
032 * @see JXFormattedTextField
033 * @see JXTextArea
034 * @see PromptTextUI
035 * 
036 * @author Peter Weishapl <petw@gmx.net>
037 * @author Karl Schaefer
038 */
039public final class PromptSupport {
040        /**
041         * The prompt text property.
042         */
043        public static final String PROMPT = "promptText";
044
045        /**
046         * The color of the prompt text property.
047         */
048        public static final String FOREGROUND = "promptForeground";
049
050        /**
051         * The prompt background property.
052         */
053        public static final String BACKGROUND = "promptBackground";
054        
055        /**
056         * The prompt background property.
057         */
058        public static final String BACKGROUND_PAINTER = "promptBackgroundPainter";
059
060        /**
061         * The focus behavior property.
062         */
063        public static final String FOCUS_BEHAVIOR = "focusBehavior";
064
065        /**
066         * The font style property, if different from the components font.
067         */
068        public static final String FONT_STYLE = "promptFontStyle";
069
070        /**
071         * <p>
072         * Determines how the {@link JTextComponent} is rendered when focused and no
073         * text is present.
074         * </p>
075         */
076        public static enum FocusBehavior {
077                /**
078                 * Keep the prompt text visible.
079                 */
080                SHOW_PROMPT,
081                /**
082                 * Highlight the prompt text as it would be selected.
083                 */
084                HIGHLIGHT_PROMPT,
085                /**
086                 * Hide the prompt text.
087                 */
088                HIDE_PROMPT
089        };
090        
091        private PromptSupport() {
092            //prevent instantiation
093        }
094
095        /**
096         * <p>
097         * Convenience method to set the <code>promptText</code> and
098         * <code>promptTextColor</code> on a {@link JTextComponent}.
099         * </p>
100         * <p>
101         * If <code>stayOnUIChange</code> is true, The prompt support will stay
102         * installed, even when the text components UI changes. See
103         * {@link #install(JTextComponent, boolean)}.
104         * </p>
105         * 
106         * @param promptText
107         * @param promptForeground
108         * @param promptBackground
109         * @param textComponent
110         */
111        public static void init(String promptText, Color promptForeground, Color promptBackground,
112                        final JTextComponent textComponent) {
113                if (promptText != null && promptText.length() > 0) {
114                        setPrompt(promptText, textComponent);
115                }
116                if (promptForeground != null) {
117                        setForeground(promptForeground, textComponent);
118                }
119                if (promptBackground != null) {
120                        setBackground(promptBackground, textComponent);
121                }
122        }
123
124        /**
125         * Get the {@link FocusBehavior} of <code>textComponent</code>.
126         * 
127         * @param textComponent
128         * @return the {@link FocusBehavior} or {@link FocusBehavior#HIDE_PROMPT} if
129         *         none is set
130         */
131        public static FocusBehavior getFocusBehavior(JTextComponent textComponent) {
132                FocusBehavior fb = (FocusBehavior) textComponent.getClientProperty(FOCUS_BEHAVIOR);
133                if (fb == null) {
134                        fb = FocusBehavior.HIDE_PROMPT;
135                }
136                return fb;
137        }
138
139        /**
140         * Sets the {@link FocusBehavior} on <code>textComponent</code> and
141         * repaints the component to reflect the changes, if it is the focus owner.
142         * 
143         * @param focusBehavior
144         * @param textComponent
145         */
146        public static void setFocusBehavior(FocusBehavior focusBehavior, JTextComponent textComponent) {
147                textComponent.putClientProperty(FOCUS_BEHAVIOR, focusBehavior);
148                if (textComponent.isFocusOwner()) {
149                        textComponent.repaint();
150                }
151        }
152
153        /**
154         * Get the prompt text of <code>textComponent</code>.
155         * 
156         * @param textComponent
157         * @return the prompt text
158         */
159        public static String getPrompt(JTextComponent textComponent) {
160                return (String) textComponent.getClientProperty(PROMPT);
161        }
162
163        /**
164         * <p>
165         * Sets the prompt text on <code>textComponent</code>. Also sets the
166         * tooltip text to the prompt text if <code>textComponent</code> has no
167         * tooltip text or the current tooltip text is the same as the current
168         * prompt text.
169         * </p>
170         * <p>
171         * Calls {@link #install(JTextComponent)} to ensure that the
172         * <code>textComponent</code>s UI is wrapped by the appropriate
173         * {@link PromptTextUI}.
174         * </p>
175         * 
176         * @param promptText
177         * @param textComponent
178         */
179        public static void setPrompt(String promptText, JTextComponent textComponent) {
180                TextUIWrapper.getDefaultWrapper().install(textComponent, true);
181
182                // display prompt as tooltip by default
183                if (textComponent.getToolTipText() == null || textComponent.getToolTipText().equals(getPrompt(textComponent))) {
184                        textComponent.setToolTipText(promptText);
185                }
186
187                textComponent.putClientProperty(PROMPT, promptText);
188                textComponent.repaint();
189        }
190
191        /**
192         * Get the foreground color of the prompt text. If no color has been set,
193         * the <code>textComponent</code>s disabled text color will be returned.
194         * 
195         * @param textComponent
196         * @return the color of the prompt text or
197         *         {@link JTextComponent#getDisabledTextColor()} if none is set
198         */
199        public static Color getForeground(JTextComponent textComponent) {
200                if (textComponent.getClientProperty(FOREGROUND) == null) {
201                        return textComponent.getDisabledTextColor();
202                }
203                return (Color) textComponent.getClientProperty(FOREGROUND);
204        }
205
206        /**
207         * Sets the foreground color of the prompt on <code>textComponent</code>
208         * and repaints the component to reflect the changes. This color will be
209         * used when no text is present.
210         * 
211         * @param promptTextColor
212         * @param textComponent
213         */
214        public static void setForeground(Color promptTextColor, JTextComponent textComponent) {
215                textComponent.putClientProperty(FOREGROUND, promptTextColor);
216                textComponent.repaint();
217        }
218
219        /**
220         * Get the background color of the <code>textComponent</code>, when no
221         * text is present. If no color has been set, the <code>textComponent</code>s
222         * background color color will be returned.
223         * 
224         * @param textComponent
225         * @return the the background color of the text component, when no text is
226         *         present
227         */
228        public static Color getBackground(JTextComponent textComponent) {
229                if (textComponent.getClientProperty(BACKGROUND) == null) {
230                        return textComponent.getBackground();
231                }
232                return (Color) textComponent.getClientProperty(BACKGROUND);
233        }
234
235        /**
236         * <p>
237         * Sets the prompts background color on <code>textComponent</code> and
238         * repaints the component to reflect the changes. This background color will
239         * only be used when no text is present.
240         * </p>
241         * <p>
242         * Calls {@link #install(JTextComponent)} to ensure that the
243         * <code>textComponent</code>s UI is wrapped by the appropriate
244         * {@link PromptTextUI}.
245         * </p>
246         * 
247         * @param background
248         * @param textComponent
249         */
250        public static void setBackground(Color background, JTextComponent textComponent) {
251                TextUIWrapper.getDefaultWrapper().install(textComponent, true);
252
253                textComponent.putClientProperty(BACKGROUND, background);
254                textComponent.repaint();
255        }
256        
257        /**
258         * Get the background painter of the <code>textComponent</code>, when no
259         * text is present. If no painter has been set, then {@code null} will be returned.
260         * 
261         * @param textComponent
262         * @return the background painter of the text component
263         */
264        @SuppressWarnings("unchecked")
265    public static <T extends JTextComponent> Painter<? super T> getBackgroundPainter(T textComponent) {
266            Painter<? super T> painter = (Painter<? super T>) textComponent.getClientProperty(BACKGROUND_PAINTER);
267            
268            if (painter == null) {
269                painter = Painters.EMPTY_PAINTER;
270            }
271            
272            return painter;
273        }
274        
275        /**
276         * <p>
277         * Sets the prompts background painter on <code>textComponent</code> and
278         * repaints the component to reflect the changes. This background painter will
279         * only be used when no text is present.
280         * </p>
281         * <p>
282         * Calls {@link #install(JTextComponent)} to ensure that the
283         * <code>textComponent</code>s UI is wrapped by the appropriate
284         * {@link PromptTextUI}.
285         * </p>
286         * 
287         * @param background
288         * @param textComponent
289         */
290        public static <T extends JTextComponent> void setBackgroundPainter(Painter<? super T> background, T textComponent) {
291            TextUIWrapper.getDefaultWrapper().install(textComponent, true);
292            
293            textComponent.putClientProperty(BACKGROUND_PAINTER, background);
294            textComponent.repaint();
295        }
296
297        /**
298         * <p>
299         * Set the style of the prompt font, if different from the
300         * <code>textComponent</code>s font.
301         * </p>
302         * <p>
303         * Allowed values are {@link Font#PLAIN}, {@link Font#ITALIC},
304         * {@link Font#BOLD}, a combination of {@link Font#BOLD} and
305         * {@link Font#ITALIC} or <code>null</code> if the prompt font should be
306         * the same as the <code>textComponent</code>s font.
307         * </p>
308         * 
309         * @param fontStyle
310         * @param textComponent
311         */
312        public static void setFontStyle(Integer fontStyle, JTextComponent textComponent) {
313                textComponent.putClientProperty(FONT_STYLE, fontStyle);
314                textComponent.revalidate();
315                textComponent.repaint();
316        }
317
318        /**
319         * Returns the font style of the prompt text, or <code>null</code> if the
320         * prompt's font style should not differ from the <code>textComponent</code>s
321         * font.
322         * 
323         * @param textComponent
324         * @return font style of the prompt text
325         */
326        public static Integer getFontStyle(JTextComponent textComponent) {
327                return (Integer) textComponent.getClientProperty(FONT_STYLE);
328        }
329}