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