001 /*
002 * $Id: JXStatusBar.java,v 1.6 2006/05/14 08:12:18 dmouse Exp $
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.Component;
026 import java.awt.Container;
027 import java.awt.Dimension;
028 import java.awt.Insets;
029 import java.awt.LayoutManager2;
030 import java.util.HashMap;
031 import java.util.Map;
032 import javax.swing.JSeparator;
033 import javax.swing.SwingConstants;
034 import org.jdesktop.swingx.plaf.JXStatusBarAddon;
035 import org.jdesktop.swingx.plaf.LookAndFeelAddons;
036 import org.jdesktop.swingx.plaf.StatusBarUI;
037
038 /**
039 * <p>A container for {@link javax.swing.JComponent}s that is typically placed at
040 * the bottom of a form and runs the entire width of the form. There are 3
041 * important functions that JXStatusBar provides (reasons why we created it
042 * in the first place). First, JXStatusBar provides a hook for a pluggable look.
043 * There is a definite look associated with status bars on windows, for instance.
044 * By implementing a concrete subclass of JXPanel, we provide a way for the
045 * pluggable look and feel system to modify the look of the status bar automatically.</p>
046 *
047 * <p>Second, JXStatusBar comes with its own layout manager. Each item is added to
048 * the JXStatusBar with a JXStatusBar.Constraint as the constraint argument. The
049 * JXStatusBar.Constraint contains an Insets object, as well as a "weight". The weight
050 * is used the same as the GridBagLayout.</p>
051 *
052 * <p>Finally, JXStatusBar contains a built in JPopupMenu. This menu allows the
053 * user to hide/show any of the items on the status bar. It also may optionally
054 * contain an "add..." action that will allow the user to select from a handful
055 * of prepacked beans that can be placed on the status bar.</p>
056 *
057 * <p>Constructing a JXStatusBar is very straitforward:
058 * <pre><code>
059 * JXStatusBar bar = new JXStatusBar();
060 * JLabel statusLabel = new JLabel("Ready");
061 * bar.add(statusLabel, new JXStatusBar.Constraints(1.0); //weight of 0.0 and no insets
062 * JProgressBar pbar = new JProgressBar();
063 * bar.add(pbar); //weight of 0.0 and no insets
064 * </code></pre></p>
065 *
066 * <p>Two common use cases for status bars is tracking application status and
067 * progress. JXStatusBar does not manage these tasks, but instead special components
068 * exist or can be created that do manage these tasks. For instance, if you application
069 * has a TaskManager or some other repository of currently running jobs, you could
070 * easily create a TaskManagerProgressBar that tracks those jobs. This component
071 * could then be added to the JXStatusBar like any other component.</p>
072 *
073 * @author pdoubleya
074 * @author rbair
075 */
076 public class JXStatusBar extends JXPanel {
077 /**
078 * @see #getUIClassID // *
079 * @see #readObject
080 */
081 static public final String uiClassID = "StatusBarUI";
082
083 /**
084 * Initialization that would ideally be moved into various look and feel
085 * classes.
086 */
087 static {
088 LookAndFeelAddons.contribute(new JXStatusBarAddon());
089 }
090
091 // //TODO this should be part of the plaf UI delegate
092 // private Insets margin = new Insets(3, 3, 3, 3);
093
094 public JXStatusBar() {
095 super();
096 setLayout(new Layout());
097 }
098
099 /**
100 * Returns the look and feel (L&F) object that renders this component.
101 *
102 * @return the StatusBarUI object that renders this component
103 */
104 @Override
105 public StatusBarUI getUI() {
106 return (StatusBarUI) ui;
107 }
108
109 /**
110 * Sets the look and feel (L&F) object that renders this component.
111 *
112 * @param ui
113 * the StatusBarUI L&F object
114 * @see javax.swing.UIDefaults#getUI
115 * @beaninfo bound: true hidden: true attribute: visualUpdate true
116 * description: The UI object that implements the Component's
117 * LookAndFeel.
118 */
119 public void setUI(StatusBarUI ui) {
120 super.setUI(ui);
121 }
122
123 /**
124 * Returns a string that specifies the name of the L&F class that renders
125 * this component.
126 *
127 * @return "StatusBarUI"
128 * @see javax.swing.JComponent#getUIClassID
129 * @see javax.swing.UIDefaults#getUI
130 * @beaninfo expert: true description: A string that specifies the name of
131 * the L&F class.
132 */
133 @Override
134 public String getUIClassID() {
135 return uiClassID;
136 }
137
138 /**
139 * Notification from the <code>UIManager</code> that the L&F has changed.
140 * Replaces the current UI object with the latest version from the
141 * <code>UIManager</code>.
142 *
143 * @see javax.swing.JComponent#updateUI
144 */
145 @Override
146 public void updateUI() {
147 setUI((StatusBarUI) LookAndFeelAddons
148 .getUI(this, StatusBarUI.class));
149 }
150
151 // @Override
152 // public Insets getInsets() {
153 // Insets i = super.getInsets();
154 // i.top += margin.top;
155 // i.left += margin.left;
156 // i.right += margin.right;
157 // i.bottom += margin.bottom;
158 // return i;
159 // }
160 //
161 // @Override
162 // public Insets getInsets(Insets insets) {
163 // insets = super.getInsets(insets);
164 // insets.top += margin.top;
165 // insets.left += margin.left;
166 // insets.right += margin.right;
167 // insets.bottom += margin.bottom;
168 // return insets;
169 // }
170
171 public void addSeparator() {
172 JSeparator sep = new JSeparator(SwingConstants.VERTICAL);
173 add(sep, new Constraint(new Insets(1, 5, 1, 5)));
174 }
175
176 public static class Constraint {
177 private Insets insets;
178 private double weight;
179
180 public Constraint(Insets insets) {
181 this(0.0, insets);
182 }
183
184 public Constraint(double weight) {
185 this(weight, new Insets(0,0,0,0));
186 }
187
188 public Constraint(double weight, Insets insets) {
189 this.weight = weight;
190 this.insets = insets;
191 }
192
193 public double getWeight() {
194 return weight;
195 }
196
197 public Insets getInsets() {
198 return new Insets(insets.top, insets.left, insets.bottom, insets.right);
199 }
200 }
201
202 private static class Layout implements LayoutManager2 {
203 private Map<Component,Constraint> constraints = new HashMap<Component,Constraint>();
204
205 public void addLayoutComponent(String name, Component comp) {
206 addLayoutComponent(comp, null);
207 }
208
209 public void addLayoutComponent(Component comp, Object constraint) {
210 constraints.put(comp, (Constraint)constraint);
211 }
212
213 public void removeLayoutComponent(Component comp) {
214 constraints.remove(comp);
215 }
216
217 public Dimension preferredLayoutSize(Container parent) {
218 Dimension prefSize = new Dimension();
219 for (Component comp : constraints.keySet()) {
220 Dimension d = comp.getPreferredSize();
221 Constraint c = constraints.get(comp);
222 if (c != null) {
223 Insets i = c.getInsets();
224 d.width += i.left + i.right;
225 d.height += i.top + i.bottom;
226 }
227 prefSize.height = Math.max(prefSize.height, d.height);
228 prefSize.width += d.width;
229 }
230
231 Insets insets = parent.getInsets();
232 prefSize.height += insets.top + insets.bottom;
233 prefSize.width += insets.left + insets.right;
234 return prefSize;
235 }
236
237 public Dimension minimumLayoutSize(Container parent) {
238 return preferredLayoutSize(parent);
239 }
240
241 public Dimension maximumLayoutSize(Container target) {
242 return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
243 }
244
245 public float getLayoutAlignmentX(Container target) {
246 return .5f;
247 }
248
249 public float getLayoutAlignmentY(Container target) {
250 return .5f;
251 }
252
253 public void invalidateLayout(Container target) {
254 //I don't hold on to any state, so nothing to do here
255 }
256
257 public void layoutContainer(Container parent) {
258 //find out the maximum weight of all the visible components
259 double maxWeight = 0.0;
260 for (Component comp : parent.getComponents()) {
261 if (comp.isVisible()) {
262 Constraint c = constraints.get(comp);
263 maxWeight += c == null ? 0.0 : c.getWeight();
264 }
265 }
266 maxWeight = maxWeight == 0 ? 1.0 : maxWeight; //don't let maxWeight be 0
267
268 //the amount of available space. If positive, it will be split up among
269 //all visible components that have a positive weight
270 //If negative, then no weights will be configured
271 Insets parentInsets = parent.getInsets();
272 int availableSpace = parent.getWidth() - preferredLayoutSize(parent).width;
273 //the next X location to place a component at
274 int nextX = parentInsets.left;
275 int height = parent.getHeight() - parentInsets.top - parentInsets.bottom;
276
277 //now lay out each visible component
278 for (Component comp : parent.getComponents()) {
279 if (comp.isVisible()) {
280 Constraint c = constraints.get(comp);
281 double weight = c == null ? 0.0 : c.getWeight();
282 Insets insets = c == null ? new Insets(0,0,0,0) : c.getInsets();
283
284 int spaceToTake = availableSpace > 0 ?
285 (int)((weight/maxWeight) * availableSpace) : 0;
286 availableSpace -= spaceToTake;
287
288 int width = comp.getPreferredSize().width + spaceToTake;
289
290 int x = nextX + insets.left;
291 int y = parentInsets.top + insets.top;
292 comp.setSize(width, height);
293 comp.setLocation(x, y);
294 nextX = x + width + insets.right;
295 }
296 }
297 }
298 }
299 }