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 }