001/*
002 * $Id: JXStatusBar.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
024
025import java.awt.Insets;
026
027import javax.swing.JComponent;
028
029import org.jdesktop.beans.JavaBean;
030import org.jdesktop.swingx.plaf.LookAndFeelAddons;
031import org.jdesktop.swingx.plaf.StatusBarAddon;
032import org.jdesktop.swingx.plaf.StatusBarUI;
033
034/**
035 * <p>A container for <code>JComponents</code> that is typically placed at
036 * the bottom of a form and runs the entire width of the form. There are 3
037 * important functions that <code>JXStatusBar</code> provides.
038 * First, <code>JXStatusBar</code> provides a hook for a pluggable look.
039 * There is a definite look associated with status bars on windows, for instance.
040 * By implementing a subclass of {@link JComponent}, we provide a way for the
041 * pluggable look and feel system to modify the look of the status bar.</p>
042 *
043 * <p>Second, <code>JXStatusBar</code> comes with its own layout manager. Each item is added to
044 * the <code>JXStatusBar</code> with a <code>JXStatusBar.Constraint</code>
045 * as the constraint argument. The <code>JXStatusBar.Constraint</code> contains 
046 * an <code>Insets</code> object, as well as a <code>ResizeBehavior</code>, 
047 * which can be FIXED or FILL. The resize behaviour applies to the width of
048 * components. All components added will maintain there preferred height, and the
049 * height of the <code>JXStatusBar</code> will be the height of the highest 
050 * component plus insets.</p>
051 *  
052 * <p>A constraint with <code>JXStatusBar.Constraint.ResizeBehavior.FIXED</code>
053 * will cause the component to occupy a fixed area on the <code>JXStatusBar</code>.
054 * The size of the area remains constant when the <code>JXStatusBar</code> is resized.
055 * A constraint with this behavior may also take a width value, see 
056 * {@link JXStatusBar.Constraint#setFixedWidth(int)}. The width is a preferred
057 * minimum width. If the component preferred width is greater than the constraint
058 * width, the component width will apply.</p>
059 * 
060 * <p>All components with constraint <code>JXStatusBar.Constraint.ResizeBehavior.FILL</code>
061 * will share equally any spare space in the <code>JXStatusBar</code>. Spare space
062 * is that left over after allowing for all FIXED component and the preferred 
063 * width of FILL components, plus insets  
064 * 
065 * <p>Constructing a <code>JXStatusBar</code> is very straightforward:
066 * <pre><code>
067 *      JXStatusBar bar = new JXStatusBar();
068 *      JLabel statusLabel = new JLabel("Ready");
069 *      JXStatusBar.Constraint c1 = new JXStatusBar.Constraint() 
070 *      c1.setFixedWidth(100);
071 *      bar.add(statusLabel, c1);     // Fixed width of 100 with no inserts
072 *      JXStatusBar.Constraint c2 = new JXStatusBarConstraint(
073 *              JXStatusBar.Constraint.ResizeBehavior.FILL) // Fill with no inserts
074 *      JProgressBar pbar = new JProgressBar();
075 *      bar.add(pbar, c2);            // Fill with no inserts - will use remaining space
076 * </code></pre></p>
077 *
078 * <p>Two common use cases for status bars include tracking application status and
079 * progress. <code>JXStatusBar</code> does not manage these tasks, but instead special components
080 * exist or can be created that do manage these tasks. For example, if your application
081 * has a TaskManager or some other repository of currently running jobs, you could
082 * easily create a TaskManagerProgressBar that tracks those jobs. This component
083 * could then be added to the <code>JXStatusBar</code> like any other component.</p>
084 *
085 * <h2>Client Properties</h2>
086 * <p>The BasicStatusBarUI.AUTO_ADD_SEPARATOR client property can be specified, which
087 *    will disable the auto-adding of separators. In this case, you must add your own
088 *    JSeparator components. To use:
089 * <pre><code>
090 *      JXStatusBar sbar = new JXStatusBar();
091 *      sbar.putClientProperty(BasicStatusBarUI.AUTO_ADD_SEPARATOR, false);
092 *      sbar.add(comp1);
093 *      sbar.add(new JSeparator(JSeparator.VERTICAL));
094 *      sbar.add(comp2);
095 *      sbar.add(comp3);
096 *  </code></pre></p>
097 *
098 * @status REVIEWED
099 *
100 * @author pdoubleya
101 * @author rbair
102 * @author Karl George Schaefer
103 */
104@JavaBean
105public class JXStatusBar extends JComponent {
106    /**
107     * @see #getUIClassID
108     * @see #readObject
109     */
110    public static final String uiClassID = "StatusBarUI";
111    
112    //TODO how to handle UI delegate setting of primitive?
113    private boolean resizeHandleEnabled;
114    
115    /**
116     * Initialization that would ideally be moved into various look and feel
117     * classes.
118     */
119    static {
120        LookAndFeelAddons.contribute(new StatusBarAddon());
121    }
122    
123    /**
124     * Creates a new JXStatusBar
125     */
126    public JXStatusBar() {
127        super();
128        updateUI();
129    }
130
131    /**
132     * @param resizeHandleEnabled the resizeHandleEnabled to set
133     */
134    public void setResizeHandleEnabled(boolean resizeHandleEnabled) {
135        boolean oldValue = isResizeHandleEnabled();
136        this.resizeHandleEnabled = resizeHandleEnabled;
137        firePropertyChange("resizeHandleEnabled", oldValue, isResizeHandleEnabled());
138    }
139
140    /**
141     * @return the resizeHandleEnabled
142     */
143    public boolean isResizeHandleEnabled() {
144        return resizeHandleEnabled;
145    }
146
147    /**
148     * Returns the look and feel (L&F) object that renders this component.
149     * 
150     * @return the StatusBarUI object that renders this component
151     */
152    public StatusBarUI getUI() {
153        return (StatusBarUI) ui;
154    }
155
156    /**
157     * Sets the look and feel (L&F) object that renders this component.
158     * 
159     * @param ui
160     *            the StatusBarUI L&F object
161     * @see javax.swing.UIDefaults#getUI
162     * @beaninfo
163     *        bound: true
164     *       hidden: true
165     *    attribute: visualUpdate true
166     *  description: The component's look and feel delegate.
167     */
168    public void setUI(StatusBarUI ui) {
169        super.setUI(ui);
170    }
171
172    /**
173     * Returns a string that specifies the name of the L&F class that renders
174     * this component.
175     * 
176     * @return "StatusBarUI"
177     * @see javax.swing.JComponent#getUIClassID
178     * @see javax.swing.UIDefaults#getUI
179     * @beaninfo expert: true description: A string that specifies the name of
180     *           the L&F class.
181     */
182    @Override
183    public String getUIClassID() {
184        return uiClassID;
185    }
186
187    /**
188     * Notification from the <code>UIManager</code> that the L&F has changed.
189     * Replaces the current UI object with the latest version from the
190     * <code>UIManager</code>.
191     * 
192     * @see javax.swing.JComponent#updateUI
193     */
194    @Override
195    public void updateUI() {
196        setUI((StatusBarUI) LookAndFeelAddons
197                .getUI(this, StatusBarUI.class));
198    }
199
200    /**
201     * The constraint object to be used with the <code>JXStatusBar</code>. It takes
202     * a ResizeBehaviour, Insets and a Width. Width is only applicable for  
203     * ResizeBehavior.FIXED. @see JXStatusBar class documentation.
204     */
205    public static class Constraint {
206        public static enum ResizeBehavior {FILL, FIXED}
207        
208        private Insets insets;
209        private ResizeBehavior resizeBehavior;
210        private int fixedWidth = 0;
211        
212        /**
213         * Creates a new Constraint with default FIXED behaviour and no insets.
214         */
215        public Constraint() {
216            this(ResizeBehavior.FIXED, null);
217        }
218        
219        /**
220         * Creates a new Constraint with default FIXED behaviour and the given insets
221         * 
222         * @param insets may be null. If null, an Insets with 0 values will be used.
223         */
224        public Constraint(Insets insets) {
225            this(ResizeBehavior.FIXED, insets);
226        }
227        
228        /**
229         * Creates a new Constraint with default FIXED behaviour and the given fixed
230         * width.
231         * 
232         * @param fixedWidth must be >= 0
233         */
234        public Constraint(int fixedWidth) {
235            this(fixedWidth, null);
236        }
237        
238        /**
239         * Creates a new Constraint with default FIXED behaviour and the given fixed
240         * width, and using the given Insets.
241         * 
242         * @param fixedWidth must be >= 0
243         * @param insets may be null. If null, an Insets with 0 values will be used.
244         */
245        public Constraint(int fixedWidth, Insets insets) {
246            if (fixedWidth < 0) {
247                throw new IllegalArgumentException("fixedWidth must be >= 0");
248            }
249            this.fixedWidth = fixedWidth;
250            this.insets = insets == null ? new Insets(0, 0, 0, 0) : (Insets)insets.clone();
251            this.resizeBehavior = ResizeBehavior.FIXED;
252        }
253        
254        /**
255         * Creates a new Constraint with the specified resize behaviour and no insets
256         * 
257         * @param resizeBehavior - either JXStatusBar.Constraint.ResizeBehavior.FIXED
258         * or JXStatusBar.Constraint.ResizeBehavior.FILL.
259         */
260        public Constraint(ResizeBehavior resizeBehavior) {
261            this(resizeBehavior, null);
262        }
263        
264        /**
265         * Creates a new Constraint with the specified resize behavior and insets.
266         * 
267         * @param resizeBehavior - either JXStatusBar.Constraint.ResizeBehavior.FIXED
268         * or JXStatusBar.Constraints.ResizeBehavior.FILL.
269         * @param insets may be null. If null, an Insets with 0 values will be used.
270         */
271        public Constraint(ResizeBehavior resizeBehavior, Insets insets) {
272            this.resizeBehavior = resizeBehavior;
273            this.insets = insets == null ? new Insets(0, 0, 0, 0) : (Insets)insets.clone();
274        }
275        
276        /**
277         * Set the fixed width the component added with this 
278         * constraint will occupy on the <code>JXStatusBar</code>. Only applies
279         * to ResizeBehavior.FIXED. Will be ignored for ResizeBehavior.FILL.
280         *  
281         * @param width - minimum width component will occupy. If 0, the preferred
282         * width of the component will be used.
283         * The width specified must be >= 0
284         */
285        public void setFixedWidth(int width) {
286            if (width < 0) {
287                throw new IllegalArgumentException("width must be >= 0");
288            }
289            fixedWidth = resizeBehavior == ResizeBehavior.FIXED ? width : 0;
290        }
291        
292        /**
293         * Returns the ResizeBehavior.
294         * 
295         * @return ResizeBehavior
296         */
297        public ResizeBehavior getResizeBehavior() {
298            return resizeBehavior;
299        }
300        
301        /**
302         * Returns the insets.
303         * 
304         * @return insets
305         */
306        public Insets getInsets() {
307            return (Insets)insets.clone();
308        }
309        
310        /**
311         * Get fixed width. Width is zero for resize behavior FILLED
312         * @return the width of this constraint
313         */
314        public int getFixedWidth() {
315            return fixedWidth;
316        }
317    }
318    
319}