001/* 002 * Copyright (c) 2002-2007 JGoodies Karsten Lentzsch. All Rights Reserved. 003 * 004 * Redistribution and use in source and binary forms, with or without 005 * modification, are permitted provided that the following conditions are met: 006 * 007 * o Redistributions of source code must retain the above copyright notice, 008 * this list of conditions and the following disclaimer. 009 * 010 * o Redistributions in binary form must reproduce the above copyright notice, 011 * this list of conditions and the following disclaimer in the documentation 012 * and/or other materials provided with the distribution. 013 * 014 * o Neither the name of JGoodies Karsten Lentzsch nor the names of 015 * its contributors may be used to endorse or promote products derived 016 * from this software without specific prior written permission. 017 * 018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 019 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 020 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 021 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 022 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 023 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 024 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 025 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 026 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 027 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 028 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030 031package com.jgoodies.forms.builder; 032 033import javax.swing.JButton; 034import javax.swing.JComponent; 035import javax.swing.JPanel; 036 037import com.jgoodies.forms.factories.Borders; 038import com.jgoodies.forms.factories.FormFactory; 039import com.jgoodies.forms.layout.ColumnSpec; 040import com.jgoodies.forms.layout.ConstantSize; 041import com.jgoodies.forms.layout.FormLayout; 042import com.jgoodies.forms.layout.FormSpec; 043import com.jgoodies.forms.layout.RowSpec; 044import com.jgoodies.forms.util.LayoutStyle; 045 046/** 047 * A non-visual builder that assists you in building consistent button bars 048 * that comply with popular UI style guides. It utilizes the {@link FormLayout}. 049 * This class is in turn used by the 050 * {@link com.jgoodies.forms.factories.ButtonBarFactory} that provides 051 * an even higher level of abstraction for building consistent button bars.<p> 052 * 053 * Buttons added to the builder are either gridded or fixed and may fill 054 * their FormLayout cell or not. All gridded buttons get the same width, 055 * while fixed buttons use their own size. Gridded buttons honor 056 * the default minimum button width as specified by the current 057 * {@link com.jgoodies.forms.util.LayoutStyle}.<p> 058 * 059 * You can set an optional hint for narrow margin for the fixed width buttons. 060 * This is useful if you want to lay out a button bar that includes a button 061 * with a long text. For example, in a bar with 062 * 'Copy to Clipboard', 'OK', 'Cancel' you may declare the clipboard button 063 * as a fixed size button with narrow margins, OK and Cancel as gridded. 064 * Gridded buttons are marked as narrow by default. 065 * Note that some look&feels do not support the narrow margin feature, 066 * and conversely, others have only narrow margins. The JGoodies look&feels 067 * honor the setting, the Mac Aqua l&f uses narrow margins all the time.<p> 068 * 069 * To honor the platform's button order (left-to-right vs. right-to-left) 070 * this builder uses the <em>leftToRightButtonOrder</em> property. 071 * It is initialized with the current LayoutStyle's button order, 072 * which in turn is left-to-right on most platforms and right-to-left 073 * on the Mac OS X. Builder methods that create sequences of buttons 074 * (e.g. {@link #addGriddedButtons(JButton[])} honor the button order. 075 * If you want to ignore the default button order, you can either 076 * add add individual buttons, or create a ButtonBarBuilder instance 077 * with the order set to left-to-right. For the latter see 078 * {@link #createLeftToRightBuilder()}. Also see the button order 079 * example below.<p> 080 * 081 * <strong>Example:</strong><br> 082 * The following example builds a button bar with <i>Help</i> button on the 083 * left-hand side and <i>OK, Cancel, Apply</i> buttons on the right-hand side. 084 * <pre> 085 * private JPanel createHelpOKCancelApplyBar( 086 * JButton help, JButton ok, JButton cancel, JButton apply) { 087 * ButtonBarBuilder builder = new ButtonBarBuilder(); 088 * builder.addGridded(help); 089 * builder.addRelatedGap(); 090 * builder.addGlue(); 091 * builder.addGriddedButtons(new JButton[]{ok, cancel, apply}); 092 * return builder.getPanel(); 093 * } 094 * </pre><p> 095 * 096 * <strong>Button Order Example:</strong><br> 097 * The following example builds three button bars where one honors 098 * the platform's button order and the other two ignore it. 099 * <pre> 100 * public JComponent buildPanel() { 101 * FormLayout layout = new FormLayout("pref"); 102 * DefaultFormBuilder rowBuilder = new DefaultFormBuilder(layout); 103 * rowBuilder.setDefaultDialogBorder(); 104 * 105 * rowBuilder.append(buildButtonSequence(new ButtonBarBuilder())); 106 * rowBuilder.append(buildButtonSequence(ButtonBarBuilder.createLeftToRightBuilder())); 107 * rowBuilder.append(buildIndividualButtons(new ButtonBarBuilder())); 108 * 109 * return rowBuilder.getPanel(); 110 * } 111 * 112 * private Component buildButtonSequence(ButtonBarBuilder builder) { 113 * builder.addGriddedButtons(new JButton[] { 114 * new JButton("One"), 115 * new JButton("Two"), 116 * new JButton("Three") 117 * }); 118 * return builder.getPanel(); 119 * } 120 * 121 * private Component buildIndividualButtons(ButtonBarBuilder builder) { 122 * builder.addGridded(new JButton("One")); 123 * builder.addRelatedGap(); 124 * builder.addGridded(new JButton("Two")); 125 * builder.addRelatedGap(); 126 * builder.addGridded(new JButton("Three")); 127 * return builder.getPanel(); 128 * } 129 * </pre> 130 * 131 * @author Karsten Lentzsch 132 * @version $Revision$ 133 * 134 * @see ButtonStackBuilder 135 * @see com.jgoodies.forms.factories.ButtonBarFactory 136 * @see com.jgoodies.forms.util.LayoutStyle 137 */ 138public final class ButtonBarBuilder extends PanelBuilder { 139 140 /** 141 * Specifies the columns of the initial FormLayout used in constructors. 142 */ 143 private static final ColumnSpec[] COL_SPECS = new ColumnSpec[] {}; 144 145 /** 146 * Specifies the FormLayout's the single button bar row. 147 */ 148 private static final RowSpec[] ROW_SPECS = new RowSpec[] { 149 new RowSpec("center:pref") }; 150 151 /** 152 * The client property key used to indicate that a button shall 153 * get narrow margins on the left and right hand side.<p> 154 * 155 * This optional setting will be honored by all JGoodies Look&Feel 156 * implementations. The Mac Aqua l&f uses narrow margins only. 157 * Other look&feel implementations will likely ignore this key 158 * and so may render a wider button margin. 159 */ 160 private static final String NARROW_KEY = "jgoodies.isNarrow"; 161 162 /** 163 * Describes how sequences of buttons are added to the button bar: 164 * left-to-right or right-to-left. This setting is initialized using 165 * the current {@link LayoutStyle}'s button order. It is honored 166 * only by builder methods that build sequences of button, for example 167 * {@link #addGriddedButtons(JButton[])}, and ignored if you add 168 * individual button, for example using {@link #addGridded(JComponent)}. 169 * 170 * @see #isLeftToRight() 171 * @see #setLeftToRight(boolean) 172 * @see #addGriddedButtons(JButton[]) 173 * @see #addGriddedGrowingButtons(JButton[]) 174 */ 175 private boolean leftToRight; 176 177 // Instance Creation **************************************************** 178 179 /** 180 * Constructs an instance of <code>ButtonBarBuilder</code> on a 181 * <code>JPanel</code> using a preconfigured FormLayout as layout manager. 182 */ 183 public ButtonBarBuilder() { 184 this(new JPanel(null)); 185 } 186 187 /** 188 * Constructs an instance of <code>ButtonBarBuilder</code> on the given 189 * panel using a preconfigured FormLayout as layout manager. 190 * 191 * @param panel the layout container 192 */ 193 public ButtonBarBuilder(JPanel panel) { 194 super(new FormLayout(COL_SPECS, ROW_SPECS), panel); 195 leftToRight = LayoutStyle.getCurrent().isLeftToRightButtonOrder(); 196 } 197 198 /** 199 * Creates and returns a <code>ButtonBarBuilder</code> with 200 * initialized with a left to right button order. 201 * 202 * @return a button bar builder with button order set to left-to-right 203 */ 204 public static ButtonBarBuilder createLeftToRightBuilder() { 205 ButtonBarBuilder builder = new ButtonBarBuilder(); 206 builder.setLeftToRightButtonOrder(true); 207 return builder; 208 } 209 210 // Accessing Properties ************************************************* 211 212 /** 213 * Returns whether button sequences will be ordered from 214 * left to right or from right to left. 215 * 216 * @return true if button sequences are ordered from left to right 217 * @since 1.0.3 218 * 219 * @see LayoutStyle#isLeftToRightButtonOrder() 220 */ 221 public boolean isLeftToRightButtonOrder() { 222 return leftToRight; 223 } 224 225 /** 226 * Sets the order for button sequences to either left to right, 227 * or right to left. 228 * 229 * @param newButtonOrder true if button sequences shall be ordered 230 * from left to right 231 * @since 1.0.3 232 * 233 * @see LayoutStyle#isLeftToRightButtonOrder() 234 */ 235 public void setLeftToRightButtonOrder(boolean newButtonOrder) { 236 leftToRight = newButtonOrder; 237 } 238 239 // Default Borders ****************************************************** 240 241 /** 242 * Sets a default border that has a gap in the bar's north. 243 */ 244 public void setDefaultButtonBarGapBorder() { 245 getPanel().setBorder(Borders.BUTTON_BAR_GAP_BORDER); 246 } 247 248 // Adding Components **************************************************** 249 250 /** 251 * Adds a sequence of related gridded buttons each separated by 252 * a default gap. Honors this builder's button order. If you 253 * want to use a fixed left to right order, add individual buttons. 254 * 255 * @param buttons an array of buttons to add 256 * 257 * @see LayoutStyle 258 */ 259 public void addGriddedButtons(JButton[] buttons) { 260 int length = buttons.length; 261 for (int i = 0; i < length; i++) { 262 int index = leftToRight ? i : length - 1 - i; 263 addGridded(buttons[index]); 264 if (i < buttons.length - 1) { 265 addRelatedGap(); 266 } 267 } 268 } 269 270 /** 271 * Adds a sequence of gridded buttons that grow 272 * where each is separated by a default gap. 273 * Honors this builder's button order. If you 274 * want to use a fixed left to right order, 275 * add individual buttons. 276 * 277 * @param buttons an array of buttons to add 278 * 279 * @see LayoutStyle 280 */ 281 public void addGriddedGrowingButtons(JButton[] buttons) { 282 int length = buttons.length; 283 for (int i = 0; i < length; i++) { 284 int index = leftToRight ? i : length - 1 - i; 285 addGriddedGrowing(buttons[index]); 286 if (i < buttons.length - 1) { 287 addRelatedGap(); 288 } 289 } 290 } 291 292 /** 293 * Adds a fixed size component. Unlike the gridded components, 294 * this component keeps its individual preferred dimension. 295 * 296 * @param component the component to add 297 */ 298 public void addFixed(JComponent component) { 299 getLayout().appendColumn(FormFactory.PREF_COLSPEC); 300 add(component); 301 nextColumn(); 302 } 303 304 /** 305 * Adds a fixed size component with narrow margins. Unlike the gridded 306 * components, this component keeps its individual preferred dimension. 307 * 308 * @param component the component to add 309 */ 310 public void addFixedNarrow(JComponent component) { 311 component.putClientProperty(NARROW_KEY, Boolean.TRUE); 312 addFixed(component); 313 } 314 315 /** 316 * Adds a gridded component, i.e. a component that will get 317 * the same dimension as all other gridded components. 318 * 319 * @param component the component to add 320 */ 321 public void addGridded(JComponent component) { 322 getLayout().appendColumn(FormFactory.BUTTON_COLSPEC); 323 getLayout().addGroupedColumn(getColumn()); 324 component.putClientProperty(NARROW_KEY, Boolean.TRUE); 325 add(component); 326 nextColumn(); 327 } 328 329 /** 330 * Adds a gridded component that grows. The component's initial size 331 * (before it grows) is the same as for all other gridded components. 332 * 333 * @param component the component to add 334 */ 335 public void addGriddedGrowing(JComponent component) { 336 getLayout().appendColumn(FormFactory.GROWING_BUTTON_COLSPEC); 337 getLayout().addGroupedColumn(getColumn()); 338 component.putClientProperty(NARROW_KEY, Boolean.TRUE); 339 add(component); 340 nextColumn(); 341 } 342 343 /** 344 * Adds a glue that will be given the extra space, 345 * if this box is larger than its preferred size. 346 */ 347 public void addGlue() { 348 appendGlueColumn(); 349 nextColumn(); 350 } 351 352 /** 353 * Adds the standard gap for related components. 354 */ 355 public void addRelatedGap() { 356 appendRelatedComponentsGapColumn(); 357 nextColumn(); 358 } 359 360 /** 361 * Adds the standard gap for unrelated components. 362 */ 363 public void addUnrelatedGap() { 364 appendUnrelatedComponentsGapColumn(); 365 nextColumn(); 366 } 367 368 /** 369 * Adds a strut of a specified size. 370 * 371 * @param size a <code>ConstantSize</code> that describes the gap's size 372 */ 373 public void addStrut(ConstantSize size) { 374 getLayout().appendColumn( 375 new ColumnSpec(ColumnSpec.LEFT, size, FormSpec.NO_GROW)); 376 nextColumn(); 377 } 378 379}