001/* ----------------------------------------------------------------------------
002   The Kiwi Toolkit - A Java Class Library
003   Copyright (C) 1998-2004 Mark A. Lindner
004
005   This library is free software; you can redistribute it and/or
006   modify it under the terms of the GNU General Public License as
007   published by the Free Software Foundation; either version 2 of the
008   License, or (at your option) any later version.
009
010   This library is distributed in the hope that it will be useful,
011   but WITHOUT ANY WARRANTY; without even the implied warranty of
012   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013   General Public License for more details.
014
015   You should have received a copy of the GNU General Public License
016   along with this library; if not, write to the Free Software
017   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
018   02111-1307, USA.
019 
020   The author may be contacted at: mark_a_lindner@yahoo.com
021   ----------------------------------------------------------------------------
022   $Log: ButtonPanel.java,v $
023   Revision 1.13  2004/05/12 19:08:19  markl
024   comment block updates
025
026   Revision 1.12  2003/01/19 09:50:52  markl
027   Javadoc & comment header updates.
028
029   Revision 1.11  2001/03/12 09:27:52  markl
030   Source code and Javadoc cleanup.
031
032   Revision 1.10  2000/10/11 10:45:47  markl
033   Fixes to support AbstractButtons.
034
035   Revision 1.9  1999/11/19 06:23:41  markl
036   Added getButton() method.
037
038   Revision 1.8  1999/08/04 08:09:27  markl
039   Rewrote to use a custom layout manager.
040
041   Revision 1.7  1999/07/30 03:59:18  markl
042   Bug fix.
043
044   Revision 1.6  1999/07/29 04:34:33  markl
045   Added some more sophisticated layout logic.
046
047   Revision 1.5  1999/02/28 00:27:05  markl
048   Minor fixes.
049
050   Revision 1.4  1999/02/27 12:08:17  markl
051   Minor fixes.
052
053   Revision 1.3  1999/02/27 12:06:07  markl
054   Added alignment parameter.
055
056   Revision 1.2  1999/01/10 01:00:58  markl
057   added GPL header & RCS tag
058   ----------------------------------------------------------------------------
059*/
060
061package kiwi.ui;
062
063import java.awt.*;
064import javax.swing.*;
065
066/** This class is a simple extension of <code>KPanel</code> that arranges
067  * buttons in a row, in such a way that the buttons are all of equal size,
068  * and justified flush with the right or left edge of the panel. Many Kiwi
069  * dialogs and frames provide buttons in their lower-right areas or toolbars
070  * in their upper-left areas that are positioned in just this way; this
071  * class eliminates the need to perform the layout explicitly in code each
072  * time this effect is desired.
073  * <p>
074  * The row of buttons is anchored at either the left or the right edge of
075  * the panel. If the sum of the preferred widths of the buttons exceeds the
076  * available horizontal space, the buttons are compressed horizontally to
077  * fit.
078  *
079  * @see kiwi.ui.KPanel
080  *
081  * @author Mark Lindner
082  */
083
084public class ButtonPanel extends KPanel
085  {
086  private static final int LEFT = 0, RIGHT = 1;
087  private KPanel p_buttons;
088  /** The default horizontal spacing. */
089  public static final int DEFAULT_SPACING = 5;
090
091  /** Construct a new <code>ButtonPanel</code> with the default horizontal
092    * spacing and right alignment.
093    */
094  
095  public ButtonPanel()
096    {
097    this(SwingConstants.RIGHT, DEFAULT_SPACING);
098    }
099
100  /** Construct a new <code>ButtonPanel</code> with default horizontal
101    * spacing and the given alignment.
102    *
103    * @param alignment The alignment of the buttons within their containing
104    * panel; one of <code>SwingConstants.LEFT</code> or
105    * <code>SwingConstants.RIGHT</code>.
106    */
107  
108  public ButtonPanel(int alignment)
109    {
110    this(alignment, DEFAULT_SPACING);
111    }
112  
113  /** Construct a new <code>ButtonPanel</code> with the given horizontal
114    * spacing and alignment.
115    *
116    * @param spacing The size of the gap (in pixels) to place between buttons
117    * horizontally.
118    * @param alignment The alignment of the buttons within their containing
119    * panel; one of <code>SwingConstants.LEFT</code> or
120    * <code>SwingConstants.RIGHT</code>.
121    */
122  
123  public ButtonPanel(int alignment, int spacing)
124    {
125    setOpaque(false);
126
127    int anchor = ((alignment == SwingConstants.RIGHT) ? RIGHT : LEFT);
128    setLayout(new AnchorLayout(anchor));
129
130    p_buttons = new KPanel();
131    p_buttons.setLayout(new GridLayout(1, 0, spacing, 0));
132    
133    add(p_buttons);
134    }
135
136  /** Add a button to the <code>ButtonPanel</code>.
137    *
138    * @param button The button to add.
139    * @see #removeButton
140    */
141  
142  public void addButton(AbstractButton button)
143    {
144    p_buttons.add(button);
145    }
146
147  /** Add a button to the <code>ButtonPanel</code> at the specified position.
148    *
149    * @param button The button to add.
150    * @param pos The position at which to add the button. The value 0 denotes
151    * the first position, and -1 denotes the last position.
152    * @exception java.lang.IllegalArgumentException If the value of
153    * <code>pos</code> is invalid.
154    */
155  
156  public void addButton(AbstractButton button, int pos)
157    throws IllegalArgumentException
158    {
159    p_buttons.add(button, pos);
160    }
161
162  /** Get a reference to the button at the specified position in the
163   * <code>ButtonPanel</code>.
164   *
165   * @param pos The position of the button to retrieve.
166   * @return The button.
167   */
168  
169  public AbstractButton getButton(int pos)
170    {
171    return((AbstractButton)p_buttons.getComponent(pos));
172    }
173  
174  /** Remove a button from the <code>ButtonPanel</code>.
175    *
176    * @param button The button to remove.
177    * @see #addButton
178    */
179  
180  public void removeButton(AbstractButton button)
181    {
182    p_buttons.remove(button);
183    }
184
185  /** Remove a button from the specified position in the
186    * <code>ButtonPanel</code>.
187    *
188    * @param pos The position of the button to remove, where 0 denotes the
189    * first position.
190    */
191  
192  public void removeButton(int pos)
193    {
194    p_buttons.remove(pos);
195    }
196
197  /** Get the number of buttons in this <code>ButtonPanel</code>.
198    *
199    * @return The number of buttons.
200    */
201
202  public int getButtonCount()
203    {
204    return(p_buttons.getComponentCount());
205    }
206
207  /* The custom layout manager for this component. Normally I would frown upon
208   * writing a layout manager, but in this case, none of the existing layout
209   * managers did the job, and there was no other elegant and foolproof way to
210   * achieve this effect.
211   *
212   * This layout manager can only handle one child component in a container.
213   * It anchors the child either to the right or the left edge of its parent.
214   * The child's size is set to its preferred size, if the parent is big
215   * enough. Otherwise, the child's size is set to the size of the parent.
216   */
217
218  private class AnchorLayout implements LayoutManager2
219    {
220    private int anchor;
221    private Component c = null;
222    private Dimension noSize = new Dimension(0, 0);
223
224    public AnchorLayout(int anchor)
225      {
226      this.anchor = anchor;
227      }
228    
229    public float getLayoutAlignmentX(Container cont)
230      {
231      return(0.5f);
232      }
233
234    public float getLayoutAlignmentY(Container cont)
235      {
236      return(0.5f);
237      }
238    
239    public void addLayoutComponent(String name, Component comp)
240      {
241      if(c != null)
242        return;
243
244      c = comp;
245      }
246
247    public void addLayoutComponent(Component comp, Object constraints)
248      {
249      addLayoutComponent((String)null, comp);
250      }
251
252    public void removeLayoutComponent(Component comp)
253      {
254      if(comp == c)
255        c = null;
256      }
257
258    private Dimension layoutSize(Dimension size, Insets insets)
259      {
260      if(c == null)
261        return(noSize);
262      
263      return(new Dimension((insets.left + size.width + insets.right),
264                           (insets.top + size.height + insets.bottom)));
265    }
266    
267    public Dimension maximumLayoutSize(Container cont)
268      {
269      return(c.getMaximumSize());
270      }
271
272    public void invalidateLayout(Container cont)
273      {
274      // nothing to do
275      }
276
277    // The minimum layout size is based on the component's minimum size.
278    
279    public Dimension minimumLayoutSize(Container cont)
280      {
281      return(layoutSize(c.getMinimumSize(), cont.getInsets()));
282      }
283
284    // The preferred layout size is based on the component's preferred size.
285    
286    public Dimension preferredLayoutSize(Container cont)
287      {
288      return(layoutSize(c.getPreferredSize(), cont.getInsets()));
289      }
290
291    // Lay out the container.
292    
293    public void layoutContainer(Container cont)
294      {
295      if(c == null)
296        return;
297
298      Dimension size = cont.getSize();
299      Insets insets = cont.getInsets();
300
301      int w = size.width - (insets.left + insets.right);
302      int h = size.height - (insets.top + insets.bottom);
303      Dimension p = c.getPreferredSize();
304
305      int cw = p.width;
306      
307      if(cw > w)
308        {
309        cw = w;
310        c.setSize(cw, h);
311        }
312      else
313        c.setSize(p);
314
315      int offset = ((anchor == LEFT) ? 0 : (w - cw));
316      
317      c.setLocation(insets.left + offset, insets.top);
318      }
319    }  
320
321  }
322
323/* end of source file */