001/* MetalScrollButton.java
002   Copyright (C) 2005 Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.swing.plaf.metal;
040
041import java.awt.Dimension;
042import java.awt.Graphics;
043import java.awt.Rectangle;
044
045import javax.swing.SwingUtilities;
046import javax.swing.plaf.basic.BasicArrowButton;
047
048/**
049 * A button used by the {@link MetalScrollBarUI}.  The button appearance
050 * varies according to the button direction, whether or not it is part of a 
051 * "free standing" scroll bar, and the current state of the button. 
052 */
053public class MetalScrollButton extends BasicArrowButton 
054{
055  
056  /** 
057   * The maximum size for buttons.
058   * @see #getMaximumSize()
059   */
060  private static Dimension maximumSize;     
061  
062  /** The width of the button. */
063  private int buttonWidth;
064  
065  /** 
066   * A flag that indicates whether the button is part of a free standing 
067   * scroll bar.  This affects how the border is drawn.
068   */
069  private boolean freeStanding;
070  
071  /**
072   * Creates a new button.
073   * 
074   * @param direction  the direction (this should be one of {@link #NORTH}, 
075   *                   {@link #SOUTH}, {@link #EAST} and {@link #WEST}, but 
076   *                   this is not enforced).
077   * @param width  the button width.
078   * @param freeStanding  a flag indicating whether the scroll button is free
079   *                      standing or not.
080   */
081  public MetalScrollButton(int direction, int width, boolean freeStanding)
082  {
083    super(direction);
084    buttonWidth = width;
085    this.freeStanding = freeStanding;
086    setFocusable(false);
087  }
088  
089  /**
090   * Returns the button width.
091   * 
092   * @return The button width.
093   */
094  public int getButtonWidth()
095  {
096    return buttonWidth;   
097  }
098
099  /**
100   * Sets the free standing flag.  This controls how the button border is
101   * drawn.
102   * 
103   * @param freeStanding  the new value of the flag.
104   */
105  public void setFreeStanding(boolean freeStanding)
106  {
107    this.freeStanding = freeStanding;
108  }
109  
110  /**
111   * Paints the button.
112   * 
113   * @param g  the graphics device.
114   */
115  public void paint(Graphics g)
116  {
117    Rectangle bounds = SwingUtilities.getLocalBounds(this);
118
119    // fill the background
120    if (getModel().isPressed())
121      g.setColor(MetalLookAndFeel.getControlShadow());
122    else
123      g.setColor(MetalLookAndFeel.getControl());
124    g.fillRect(0, 0, bounds.width, bounds.height);
125    
126    paintArrow(g, bounds.width, bounds.height);
127    
128    // paint a border manually - I tried using a real (custom) Border
129    // but couldn't get it to stay set for the button, something was 
130    // overwriting it...
131    if (freeStanding) 
132      {
133        if (direction == WEST)
134          paintWestBorderFreeStanding(g, bounds.width, bounds.height);        
135        else if (direction == EAST)
136          paintEastBorderFreeStanding(g, bounds.width, bounds.height);
137        else if (direction == SOUTH)
138          paintSouthBorderFreeStanding(g, bounds.width, bounds.height);
139        else // asume NORTH
140          paintNorthBorderFreeStanding(g, bounds.width, bounds.height);
141      }
142    else
143      {
144        if (direction == WEST)
145          paintWestBorder(g, bounds.width, bounds.height);        
146        else if (direction == EAST)
147          paintEastBorder(g, bounds.width, bounds.height);
148        else if (direction == SOUTH)
149          paintSouthBorder(g, bounds.width, bounds.height);
150        else // asume NORTH
151          paintNorthBorder(g, bounds.width, bounds.height);
152      }
153  }
154  
155  private void paintArrow(Graphics g, int w, int h)
156  {
157    if (isEnabled())
158      g.setColor(MetalLookAndFeel.getBlack());
159    else
160      g.setColor(MetalLookAndFeel.getControlDisabled());
161    
162    if (direction == SOUTH)
163      {
164        int x = w / 2;
165        int y = h / 2 + 2;
166        for (int i = 1; i < 5; i++)
167          g.drawLine(x - i, y - i, x + i - 1, y - i);
168      }
169    else if (direction == EAST)
170      {
171        int x = w / 2 + 2;
172        int y = h / 2;
173        for (int i = 1; i < 5; i++)
174          g.drawLine(x - i, y - i, x - i, y + i - 1);
175      }
176    else if (direction == WEST)
177      {
178        int x = w / 2 - 3;
179        int y = h / 2;
180        for (int i = 1; i < 5; i++)
181          g.drawLine(x + i, y - i, x + i, y + i - 1);        
182      }
183    else // assume NORTH
184      {
185        int x = w / 2;
186        int y = h / 2 - 3;
187        for (int i = 1; i < 5; i++)
188          g.drawLine(x - i, y + i, x + i - 1, y + i);
189      }
190  }
191  /**
192   * Paints the border for a button with a {@link #NORTH} direction that
193   * belongs to a free standing scroll bar.
194   * 
195   * @param g  the graphics device.
196   * @param w  the button width.
197   * @param h  the button height.
198   */
199  private void paintNorthBorderFreeStanding(Graphics g, int w, int h) 
200  {
201    if (isEnabled())
202      {
203        g.setColor(MetalLookAndFeel.getControlDarkShadow());
204        g.drawLine(0, 0, w - 2, 0);
205        g.drawLine(0, 0, 0, h - 1);
206        g.drawLine(2, h - 1, w - 2, h - 1);
207        g.drawLine(w - 2, 2, w - 2, h - 1);
208        
209        g.setColor(MetalLookAndFeel.getControlHighlight());
210        g.drawLine(1, 1, 1, h - 2);
211        g.drawLine(1, 1, w - 3, 1);
212        g.drawLine(w - 1, 1, w - 1, h - 1);
213      
214        g.setColor(MetalLookAndFeel.getControl());
215        g.drawLine(1, h - 1, 1, h - 1);
216        g.drawLine(w - 2, 1, w - 2, 1);
217      }
218    else
219      {
220        g.setColor(MetalLookAndFeel.getControlDisabled());
221        g.drawLine(0, 0, w - 1, 0);
222        g.drawLine(w - 1, 0, w - 1, h - 1);
223        g.drawLine(0, 0, 0, h - 1);
224      }
225  }
226  
227  /**
228   * Paints the border for a button with a {@link #SOUTH} direction that
229   * belongs to a free standing scroll bar.
230   * 
231   * @param g  the graphics device.
232   * @param w  the button width.
233   * @param h  the button height.
234   */
235  private void paintSouthBorderFreeStanding(Graphics g, int w, int h)
236  {
237    if (isEnabled())
238      {
239        g.setColor(MetalLookAndFeel.getControlDarkShadow());
240        g.drawLine(0, 0, w - 2, 0);
241        g.drawLine(0, 0, 0, h - 1);
242        g.drawLine(2, h - 1, w - 2, h - 1);
243        g.drawLine(w - 2, 2, w - 2, h - 1);
244        
245        g.setColor(MetalLookAndFeel.getControlHighlight());
246        g.drawLine(1, 1, 1, h - 1);
247        g.drawLine(1, 1, w - 1, 1);
248        g.drawLine(w - 1, 1, w - 1, h - 1);
249      
250        g.setColor(MetalLookAndFeel.getControl());
251        g.drawLine(1, h - 1, 1, h - 1);
252        g.drawLine(w - 1, 1, w - 1, 1);
253      }
254    else
255      {
256        g.setColor(MetalLookAndFeel.getControlDisabled());
257        g.drawLine(0, h - 1, w - 1, h - 1);
258        g.drawLine(w - 1, 0, w - 1, h - 1);
259        g.drawLine(0, 0, 0, h - 1);
260      }
261  }
262  
263  /**
264   * Paints the border for a button with an {@link #EAST} direction that
265   * belongs to a free standing scroll bar.
266   * 
267   * @param g  the graphics device.
268   * @param w  the button width.
269   * @param h  the button height.
270   */
271  private void paintEastBorderFreeStanding(Graphics g, int w, int h)
272  {
273    if (isEnabled())
274      {
275        g.setColor(MetalLookAndFeel.getControlDarkShadow());
276        g.drawLine(0, 0, w - 2, 0);
277        g.drawLine(w - 2, 0, w - 2, h - 2);
278        g.drawLine(0, h - 2, w - 2, h - 2);
279        
280        g.setColor(MetalLookAndFeel.getControlHighlight());
281        g.drawLine(0, 1, w - 1, 1);
282        g.drawLine(w - 1, 1, w - 1, h - 1);
283        g.drawLine(0, h - 1, w - 1, h - 1);
284      
285        g.setColor(MetalLookAndFeel.getControl());
286        g.drawLine(w - 2, 1, w - 2, 1);
287      }
288    else
289      {
290        g.setColor(MetalLookAndFeel.getControlDisabled());
291        g.drawLine(0, 0, w - 1, 0);
292        g.drawLine(w - 1, 0, w - 1, h - 1);
293        g.drawLine(0, h - 1, w - 1, h - 1);
294      }
295  }
296  
297  /**
298   * Paints the border for a button with a {@link #WEST} direction that
299   * belongs to a free standing scroll bar.
300   * 
301   * @param g  the graphics device.
302   * @param w  the button width.
303   * @param h  the button height.
304   */
305  private void paintWestBorderFreeStanding(Graphics g, int w, int h)
306  {
307    if (isEnabled())
308      {
309        g.setColor(MetalLookAndFeel.getControlDarkShadow());
310        g.drawLine(0, 0, w - 1, 0);
311        g.drawLine(0, 0, 0, h - 2);
312        g.drawLine(0, h - 2, w - 1, h - 2);
313        
314        g.setColor(MetalLookAndFeel.getControlHighlight());
315        g.drawLine(1, 1, w - 1, 1);
316        g.drawLine(1, 1, 1, h - 1);
317        g.drawLine(1, h - 1, w - 1, h - 1);
318      
319        g.setColor(MetalLookAndFeel.getControl());
320        g.drawLine(1, h - 2, 1, h - 2);
321      }
322    else
323      {
324        g.setColor(MetalLookAndFeel.getControlDisabled());
325        g.drawLine(0, 0, w - 1, 0);
326        g.drawLine(0, 0, 0, h - 1);
327        g.drawLine(0, h - 1, w - 1, h - 1);
328      }
329  }
330  
331  /**
332   * Paints the border for a button with a {@link #NORTH} direction that
333   * belongs to a scroll bar that is not free standing.
334   * 
335   * @param g  the graphics device.
336   * @param w  the button width.
337   * @param h  the button height.
338   */
339  private void paintNorthBorder(Graphics g, int w, int h) 
340  {
341    if (isEnabled())
342      {
343        g.setColor(MetalLookAndFeel.getControlDarkShadow());
344        g.drawLine(0, 0, 0, h - 1);
345         
346        g.setColor(MetalLookAndFeel.getControlHighlight());
347        g.drawLine(1, 0, 1, h - 1);
348        g.drawLine(1, 0, w - 1, 0);
349      }
350    else
351      {
352        g.setColor(MetalLookAndFeel.getControlDisabled());
353        g.drawLine(0, 0, 0, h - 1);
354      }
355  }
356  
357  /**
358   * Paints the border for a button with a {@link #SOUTH} direction that
359   * belongs to a scroll bar that is not free standing.
360   * 
361   * @param g  the graphics device.
362   * @param w  the button width.
363   * @param h  the button height.
364   */
365  private void paintSouthBorder(Graphics g, int w, int h)
366  {
367    if (isEnabled())
368      {
369        g.setColor(MetalLookAndFeel.getControlDarkShadow());
370        g.drawLine(0, 0, 0, h - 1);
371        g.drawLine(0, h - 1, w - 1, h - 1);
372         
373        g.setColor(MetalLookAndFeel.getControlHighlight());
374        g.drawLine(1, 0, 1, h - 1);
375        g.drawLine(1, 0, w - 1, 0);
376        
377        g.setColor(MetalLookAndFeel.getControl());
378        g.drawLine(1, h - 1, 1, h - 1);
379      }
380    else
381      {
382        g.setColor(MetalLookAndFeel.getControlDisabled());
383        g.drawLine(0, 0, 0, h - 1);
384      }
385  }
386
387  /**
388   * Paints the border for a button with an {@link #EAST} direction that
389   * belongs to a scroll bar that is not free standing.
390   * 
391   * @param g  the graphics device.
392   * @param w  the button width.
393   * @param h  the button height.
394   */
395  private void paintEastBorder(Graphics g, int w, int h)
396  {
397    if (isEnabled())
398      {
399        g.setColor(MetalLookAndFeel.getControlDarkShadow());
400        g.drawLine(0, 0, w - 1, 0);
401        g.drawLine(w - 1, 2, w - 1, h - 1);
402        g.setColor(MetalLookAndFeel.getControlHighlight());
403        g.drawLine(0, 1, w - 2, 1);
404        g.drawLine(0, 1, 0, h - 1);
405      }
406    else
407      {
408        g.setColor(MetalLookAndFeel.getControlDisabled());
409        g.drawLine(0, 0, w - 1, 0);
410      }
411  }
412  
413  /**
414   * Paints the border for a button with a {@link #WEST} direction that
415   * belongs to a scroll bar that is not free standing.
416   * 
417   * @param g  the graphics device.
418   * @param w  the button width.
419   * @param h  the button height.
420   */
421  private void paintWestBorder(Graphics g, int w, int h)
422  {
423    Rectangle bounds = SwingUtilities.getLocalBounds(this);
424    if (isEnabled())
425      {
426        g.setColor(MetalLookAndFeel.getControlDarkShadow());
427        g.drawLine(0, 0, bounds.width - 1, 0);
428        g.setColor(MetalLookAndFeel.getControlHighlight());
429        g.drawLine(0, 1, bounds.width - 1, 1);
430        g.drawLine(0, 1, 0, bounds.height - 1);
431      }
432    else
433      {
434        g.setColor(MetalLookAndFeel.getControlDisabled());
435        g.drawLine(0, 0, bounds.width - 1, 0);
436      }
437  }
438    
439  /**
440   * Returns the preferred size for the button, which varies depending on 
441   * the direction of the button and whether or not it is free standing.
442   * 
443   * @return The preferred size.
444   */
445  public Dimension getPreferredSize()
446  {
447    int adj = 1;
448    if (!freeStanding)
449      adj = 2;
450    
451    if (direction == EAST)
452      return new Dimension(buttonWidth - adj, buttonWidth);    
453    else if (direction == WEST)
454      return new Dimension(buttonWidth - 2, buttonWidth);
455    else if (direction == SOUTH)
456      return new Dimension(buttonWidth, buttonWidth - adj);
457    else // assume NORTH
458      return new Dimension(buttonWidth, buttonWidth - 2);
459  }
460  
461  /**
462   * Returns the minimum size for the button.
463   * 
464   * @return The minimum size for the button.
465   */
466  public Dimension getMinimumSize()
467  {
468    return getPreferredSize();
469  }
470 
471  /**
472   * Returns the maximum size for the button.
473   * 
474   * @return <code>Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE)</code>.
475   */
476  public Dimension getMaximumSize()
477  {
478    if (maximumSize == null)
479      maximumSize = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
480    return maximumSize; 
481  }
482  
483}