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