001    /*
002     * $Id: JXTitledSeparator.java 3235 2009-02-01 15:01:07Z rah003 $
003     *
004     * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005     * Santa Clara, California 95054, U.S.A. All rights reserved.
006     *
007     * This library is free software; you can redistribute it and/or
008     * modify it under the terms of the GNU Lesser General Public
009     * License as published by the Free Software Foundation; either
010     * version 2.1 of the License, or (at your option) any later version.
011     * 
012     * This library is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015     * Lesser General Public License for more details.
016     * 
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this library; if not, write to the Free Software
019     * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020     */
021    
022    package org.jdesktop.swingx;
023    
024    import java.awt.Color;
025    import java.awt.ComponentOrientation;
026    import java.awt.Font;
027    import java.awt.GridBagConstraints;
028    import java.awt.GridBagLayout;
029    import java.awt.Insets;
030    import javax.swing.Box;
031    import javax.swing.Icon;
032    import javax.swing.JLabel;
033    import javax.swing.JSeparator;
034    import javax.swing.SwingConstants;
035    import javax.swing.UIManager;
036    import javax.swing.plaf.ColorUIResource;
037    import javax.swing.plaf.FontUIResource;
038    
039    /**
040     * <p>A simple horizontal separator that contains a title.<br/>
041     *
042     * <p>JXTitledSeparator allows you to specify the title via the {@link #setTitle} method.
043     * The title alignment may be specified by using the {@link #setHorizontalAlignment}
044     * method, and accepts all the same arguments as the {@link javax.swing.JLabel#setHorizontalAlignment}
045     * method.</p>
046     *
047     * <p>In addition, you may specify an Icon to use with this separator. The icon
048     * will appear "leading" the title (on the left in left-to-right languages,
049     * on the right in right-to-left languages). To change the position of the
050     * title with respect to the icon, call {@link #setHorizontalTextPosition}.</p>
051     *
052     * <p>The default font and color of the title comes from the <code>LookAndFeel</code>, mimicking
053     * the font and color of the {@link javax.swing.border.TitledBorder}</p>
054     *
055     * <p>Here are a few example code snippets:
056     * <pre><code>
057     *  //create a plain separator
058     *  JXTitledSeparator sep = new JXTitledSeparator();
059     *  sep.setTitle("Customer Info");
060     *
061     *  //create a separator with an icon
062     *  sep = new JXTitledSeparator();
063     *  sep.setTitle("Customer Info");
064     *  sep.setIcon(new ImageIcon("myimage.png"));
065     *
066     *  //create a separator with an icon to the right of the title,
067     *  //center justified
068     *  sep = new JXTitledSeparator();
069     *  sep.setTitle("Customer Info");
070     *  sep.setIcon(new ImageIcon("myimage.png"));
071     *  sep.setHorizontalAlignment(SwingConstants.CENTER);
072     *  sep.setHorizontalTextPosition(SwingConstants.TRAILING);
073     * </code></pre>
074     *
075     * @status REVIEWED
076     * @author rbair
077     */
078    public class JXTitledSeparator extends JXPanel {
079        /**
080         * Implementation detail: the label used to display the title
081         */
082        private JLabel label;
083        /**
084         * Implementation detail: a separator to use on the left of the
085         * title if alignment is centered or right justified
086         */
087        private JSeparator leftSeparator;
088        /**
089         * Implementation detail: a separator to use on the right of the
090         * title if alignment is centered or left justified
091         */
092        private JSeparator rightSeparator;
093        
094        /** 
095         * Creates a new instance of <code>JXTitledSeparator</code>. The default title is simply
096         * an empty string. Default justification is <code>LEADING</code>, and the default
097         * horizontal text position is <code>TRAILING</code> (title follows icon)
098         */
099        public JXTitledSeparator() {
100            this("Untitled");
101        }
102        
103        /** 
104         * Creates a new instance of <code>JXTitledSeparator</code> with the specified
105         * title. Default horizontal alignment is <code>LEADING</code>, and the default
106         * horizontal text position is <code>TRAILING</code> (title follows icon)
107         */
108        public JXTitledSeparator(String title) {
109            this(title, SwingConstants.LEADING, null);
110        }
111        
112        /** 
113         * Creates a new instance of <code>JXTitledSeparator</code> with the specified
114         * title and horizontal alignment. The default
115         * horizontal text position is <code>TRAILING</code> (title follows icon)
116         */
117        public JXTitledSeparator(String title, int horizontalAlignment) {
118            this(title, horizontalAlignment, null);
119        }
120        
121        /** 
122         * Creates a new instance of <code>JXTitledSeparator</code> with the specified
123         * title, icon, and horizontal alignment. The default
124         * horizontal text position is <code>TRAILING</code> (title follows icon)
125         */
126        public JXTitledSeparator(String title, int horizontalAlignment, Icon icon) {
127            setLayout(new GridBagLayout());
128            
129            label = new JLabel(title) {
130                @Override
131                public void updateUI(){
132                  super.updateUI();
133                  updateTitle();
134                }
135            };
136            label.setIcon(icon);
137            label.setHorizontalAlignment(horizontalAlignment);
138            leftSeparator = new JSeparator();
139            rightSeparator = new JSeparator();
140            
141            layoutSeparator();
142            
143            updateTitle();
144            setOpaque(false);
145        }
146        
147        /**
148         * Implementation detail. Handles updates of title color and font on LAF change. For more 
149         * details see swingx#451.
150         */
151        //TODO remove this method in favor of UI delegate -- kgs
152        protected void updateTitle()
153        {
154          if (label == null) return;
155          
156          Color c = label.getForeground();
157          if (c == null || c instanceof ColorUIResource)
158            setForeground(UIManager.getColor("TitledBorder.titleColor"));
159    
160          Font f = label.getFont();
161          if (f == null || f instanceof FontUIResource)
162            setFont(UIManager.getFont("TitledBorder.font"));
163        }
164    
165        /**
166         * Implementation detail. lays out this component, showing/hiding components
167         * as necessary. Actually changes the containment (removes and adds components).
168         * <code>JXTitledSeparator</code> is treated as a single component rather than 
169         * a container.
170         */
171        private void layoutSeparator() {
172            removeAll();
173            
174            //SwingX #304 fix alignment issues
175            //this is really a hacky fix, but a fix nonetheless
176            //we need a better layout approach for this class
177            int alignment = getHorizontalAlignment();
178            
179            if (!getComponentOrientation().isLeftToRight()) {
180                switch (alignment) {
181                case SwingConstants.LEFT:
182                    alignment = SwingConstants.RIGHT;
183                    break;
184                case SwingConstants.RIGHT:
185                    alignment = SwingConstants.LEFT;
186                    break;
187                case SwingConstants.EAST:
188                    alignment = SwingConstants.WEST;
189                    break;
190                case SwingConstants.WEST:
191                    alignment = SwingConstants.EAST;
192                    break;
193                default:
194                    break;
195                }
196            }
197            
198            switch (alignment) {
199                case SwingConstants.LEFT:
200                case SwingConstants.LEADING:
201                case SwingConstants.WEST:
202                    add(label, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0));
203                    add(Box.createHorizontalStrut(3), new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0));
204                    add(rightSeparator, new GridBagConstraints(2, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0,0,0,0), 0, 0));
205                    break;
206                case SwingConstants.RIGHT:
207                case SwingConstants.TRAILING:
208                case SwingConstants.EAST:
209                    add(rightSeparator, new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0,0,0,0), 0, 0));
210                    add(Box.createHorizontalStrut(3), new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0));
211                    add(label, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0));
212                    break;
213                case SwingConstants.CENTER:
214                default:
215                    add(leftSeparator, new GridBagConstraints(0, 0, 1, 1, 0.5, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0,0,0,0), 0, 0));
216                    add(Box.createHorizontalStrut(3), new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0));
217                    add(label, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0));
218                    add(Box.createHorizontalStrut(3), new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0));
219                    add(rightSeparator, new GridBagConstraints(4, 0, 1, 1, 0.5, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0,0,0,0), 0, 0));
220            }
221        }
222        
223        /**
224         * Sets the title for the separator. This may be simple html, or plain
225         * text.
226         *
227         * @param title the new title. Any string input is acceptable
228         */
229        public void setTitle(String title) {
230            String old = getTitle();
231            label.setText(title);
232            firePropertyChange("title", old, getTitle());
233        }
234        
235        /**
236         * Gets the title.
237         * 
238         * @return the title being used for this <code>JXTitledSeparator</code>. 
239         *         This will be the raw title text, and so may include html tags etc 
240         *         if they were so specified in #setTitle.
241         */
242        public String getTitle() {
243            return label.getText();
244        }
245        
246        /**
247         * <p>Sets the alignment of the title along the X axis. If leading, then
248         * the title will lead the separator (in left-to-right languages,
249         * the title will be to the left and the separator to the right). If centered,
250         * then a separator will be to the left, followed by the title (centered),
251         * followed by a separator to the right. Trailing will have the title
252         * on the right with a separator to its left, in left-to-right languages.</p>
253         * 
254         * <p>LEFT and RIGHT always position the text left or right of the separator,
255         * respectively, regardless of the language orientation.</p>
256         *
257         * @param alignment  One of the following constants
258         *           defined in <code>SwingConstants</code>:
259         *           <code>LEFT</code>,
260         *           <code>CENTER</code>,
261         *           <code>RIGHT</code>,
262         *           <code>LEADING</code> (the default) or
263         *           <code>TRAILING</code>.
264         *
265         * @throws IllegalArgumentException if the alignment does not match one of
266         *         the accepted inputs.
267         * @see SwingConstants
268         * @see #getHorizontalAlignment
269         */
270        public void setHorizontalAlignment(int alignment) {
271            int old = getHorizontalAlignment();
272            label.setHorizontalAlignment(alignment);
273            if (old != getHorizontalAlignment()) {
274                layoutSeparator();
275            }
276            firePropertyChange("horizontalAlignment", old, getHorizontalAlignment());
277        }
278        
279        /**
280         * Returns the alignment of the title contents along the X axis.
281         *
282         * @return   The value of the horizontalAlignment property, one of the 
283         *           following constants defined in <code>SwingConstants</code>:
284         *           <code>LEFT</code>,
285         *           <code>CENTER</code>, 
286         *           <code>RIGHT</code>,
287         *           <code>LEADING</code> or
288         *           <code>TRAILING</code>.
289         *
290         * @see #setHorizontalAlignment
291         * @see SwingConstants
292         */
293        public int getHorizontalAlignment() {
294            return label.getHorizontalAlignment();
295        }
296    
297        /**
298         * Sets the horizontal position of the title's text,
299         * relative to the icon.
300         *
301         * @param position  One of the following constants
302         *           defined in <code>SwingConstants</code>:
303         *           <code>LEFT</code>,
304         *           <code>CENTER</code>,
305         *           <code>RIGHT</code>,
306         *           <code>LEADING</code>, or
307         *           <code>TRAILING</code> (the default).
308         * @throws IllegalArgumentException if the position does not match one of
309         *         the accepted inputs.
310         */
311        public void setHorizontalTextPosition(int position) {
312            int old = getHorizontalTextPosition();
313            label.setHorizontalTextPosition(position);
314            firePropertyChange("horizontalTextPosition", old, getHorizontalTextPosition());
315        }
316        
317        /**
318         * Returns the horizontal position of the title's text,
319         * relative to the icon.
320         *
321         * @return   One of the following constants
322         *           defined in <code>SwingConstants</code>:
323         *           <code>LEFT</code>,
324         *           <code>CENTER</code>, 
325         *           <code>RIGHT</code>,
326         *           <code>LEADING</code> or
327         *           <code>TRAILING</code>.
328         *
329         * @see SwingConstants
330         */
331        public int getHorizontalTextPosition() {
332            return label.getHorizontalTextPosition();
333        }
334        
335        /**
336         * {@inheritDoc}
337         */
338        @Override
339        public ComponentOrientation getComponentOrientation() {
340            return label.getComponentOrientation();
341        }
342    
343        /**
344         * {@inheritDoc}
345         */
346        @Override
347        public void setComponentOrientation(ComponentOrientation o) {
348            ComponentOrientation old = label.getComponentOrientation();
349            label.setComponentOrientation(o);
350            firePropertyChange("componentOrientation", old, label.getComponentOrientation());
351        }
352    
353        /**
354         * Defines the icon this component will display.  If
355         * the value of icon is null, nothing is displayed.
356         * <p>
357         * The default value of this property is null.
358         * 
359         * @see #setHorizontalTextPosition
360         * @see #getIcon
361         */
362        public void setIcon(Icon icon) {
363            Icon old = getIcon();
364            label.setIcon(icon);
365            firePropertyChange("icon", old, getIcon());
366        }
367        
368        /**
369         * Returns the graphic image (glyph, icon) that the 
370         * <code>JXTitledSeparator</code> displays.
371         *
372         * @return an Icon
373         * @see #setIcon
374         */
375        public Icon getIcon() {
376            return label.getIcon();
377        }
378    
379        /**
380         * {@inheritDoc}
381         */
382        @Override
383        public void setForeground(Color foreground) {
384            if (label != null) {
385                label.setForeground(foreground);
386            }
387            super.setForeground(foreground);
388        }
389        
390        /**
391         * {@inheritDoc}
392         */
393        @Override
394        public void setFont(Font font) {
395            if (label != null) {
396                label.setFont(font);
397            }
398            super.setFont(font);
399        }
400    }