Source for javax.swing.plaf.basic.BasicSpinnerUI

   1: /* SpinnerUI.java --
   2:    Copyright (C) 2003, 2004, 2005  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing.plaf.basic;
  40: 
  41: import java.awt.Component;
  42: import java.awt.Container;
  43: import java.awt.Dimension;
  44: import java.awt.Insets;
  45: import java.awt.LayoutManager;
  46: import java.awt.event.ActionEvent;
  47: import java.awt.event.ActionListener;
  48: import java.awt.event.MouseAdapter;
  49: import java.awt.event.MouseEvent;
  50: import java.beans.PropertyChangeEvent;
  51: import java.beans.PropertyChangeListener;
  52: 
  53: import javax.swing.JButton;
  54: import javax.swing.JComponent;
  55: import javax.swing.JSpinner;
  56: import javax.swing.LookAndFeel;
  57: import javax.swing.Timer;
  58: import javax.swing.plaf.ComponentUI;
  59: import javax.swing.plaf.SpinnerUI;
  60: 
  61: /**
  62:  * DOCUMENT ME!
  63:  *
  64:  * @author Ka-Hing Cheung
  65:  *
  66:  * @see javax.swing.JSpinner
  67:  * @since 1.4
  68:  */
  69: public class BasicSpinnerUI extends SpinnerUI
  70: {
  71:   /**
  72:    * Creates a new <code>ComponentUI</code> for the specified
  73:    * <code>JComponent</code>
  74:    *
  75:    * @param c DOCUMENT ME!
  76:    *
  77:    * @return a ComponentUI
  78:    */
  79:   public static ComponentUI createUI(JComponent c)
  80:   {
  81:     return new BasicSpinnerUI();
  82:   }
  83: 
  84:   /**
  85:    * Creates an editor component. Really, it just returns
  86:    * <code>JSpinner.getEditor()</code>
  87:    *
  88:    * @return a JComponent as an editor
  89:    *
  90:    * @see javax.swing.JSpinner#getEditor
  91:    */
  92:   protected JComponent createEditor()
  93:   {
  94:     return spinner.getEditor();
  95:   }
  96: 
  97:   /**
  98:    * Creates a <code>LayoutManager</code> that layouts the sub components. The
  99:    * subcomponents are identifies by the constraint "Next", "Previous" and
 100:    * "Editor"
 101:    *
 102:    * @return a LayoutManager
 103:    *
 104:    * @see java.awt.LayoutManager
 105:    */
 106:   protected LayoutManager createLayout()
 107:   {
 108:     return new DefaultLayoutManager();
 109:   }
 110: 
 111:   /**
 112:    * Creates the "Next" button
 113:    *
 114:    * @return the next button component
 115:    */
 116:   protected Component createNextButton()
 117:   {
 118:     JButton button = new BasicArrowButton(BasicArrowButton.NORTH);
 119:     return button;
 120:   }
 121: 
 122:   /**
 123:    * Creates the "Previous" button
 124:    *
 125:    * @return the previous button component
 126:    */
 127:   protected Component createPreviousButton()
 128:   {
 129:     JButton button = new BasicArrowButton(BasicArrowButton.SOUTH);
 130:     return button;
 131:   }
 132: 
 133:   /**
 134:    * Creates the <code>PropertyChangeListener</code> that will be attached by
 135:    * <code>installListeners</code>. It should watch for the "editor"
 136:    * property, when it's changed, replace the old editor with the new one,
 137:    * probably by calling <code>replaceEditor</code>
 138:    *
 139:    * @return a PropertyChangeListener
 140:    *
 141:    * @see #replaceEditor
 142:    */
 143:   protected PropertyChangeListener createPropertyChangeListener()
 144:   {
 145:     return new PropertyChangeListener()
 146:       {
 147:     public void propertyChange(PropertyChangeEvent evt)
 148:     {
 149:       // FIXME: Add check for enabled property change. Need to
 150:       // disable the buttons.
 151:       if ("editor".equals(evt.getPropertyName()))
 152:         BasicSpinnerUI.this.replaceEditor((JComponent) evt.getOldValue(),
 153:                                           (JComponent) evt.getNewValue());
 154:     }
 155:       };
 156:   }
 157: 
 158:   /**
 159:    * Called by <code>installUI</code>. This should set various defaults
 160:    * obtained from <code>UIManager.getLookAndFeelDefaults</code>, as well as
 161:    * set the layout obtained from <code>createLayout</code>
 162:    *
 163:    * @see javax.swing.UIManager#getLookAndFeelDefaults
 164:    * @see #createLayout
 165:    * @see #installUI
 166:    */
 167:   protected void installDefaults()
 168:   {
 169:     LookAndFeel.installColorsAndFont(spinner, "Spinner.background",
 170:                                      "Spinner.foreground", "Spinner.font");
 171:     LookAndFeel.installBorder(spinner, "Spinner.border");
 172:     spinner.setLayout(createLayout());
 173:     spinner.setOpaque(true);
 174:   }
 175: 
 176:   /*
 177:    * Called by <code>installUI</code>, which basically adds the
 178:    * <code>PropertyChangeListener</code> created by
 179:    * <code>createPropertyChangeListener</code>
 180:    *
 181:    * @see #createPropertyChangeListener
 182:    * @see #installUI
 183:    */
 184:   protected void installListeners()
 185:   {
 186:     spinner.addPropertyChangeListener(listener);
 187:   }
 188: 
 189:   /*
 190:    * Install listeners to the next button so that it increments the model
 191:    */
 192:   protected void installNextButtonListeners(Component c)
 193:   {
 194:     c.addMouseListener(new MouseAdapter()
 195:         {
 196:       public void mousePressed(MouseEvent evt)
 197:       {
 198:         if (! spinner.isEnabled())
 199:           return;
 200:         increment();
 201:         timer.setInitialDelay(500);
 202:         timer.start();
 203:       }
 204: 
 205:       public void mouseReleased(MouseEvent evt)
 206:       {
 207:         timer.stop();
 208:       }
 209: 
 210:       void increment()
 211:       {
 212:         Object next = BasicSpinnerUI.this.spinner.getNextValue();
 213:         if (next != null)
 214:           BasicSpinnerUI.this.spinner.getModel().setValue(next);
 215:       }
 216: 
 217:       volatile boolean mouseDown = false;
 218:       Timer timer = new Timer(50,
 219:                               new ActionListener()
 220:           {
 221:         public void actionPerformed(ActionEvent event)
 222:         {
 223:           increment();
 224:         }
 225:           });
 226:         });
 227:   }
 228: 
 229:   /*
 230:    * Install listeners to the previous button so that it decrements the model
 231:    */
 232:   protected void installPreviousButtonListeners(Component c)
 233:   {
 234:     c.addMouseListener(new MouseAdapter()
 235:         {
 236:       public void mousePressed(MouseEvent evt)
 237:       {
 238:         if (! spinner.isEnabled())
 239:           return;
 240:         decrement();
 241:         timer.setInitialDelay(500);
 242:         timer.start();
 243:       }
 244: 
 245:       public void mouseReleased(MouseEvent evt)
 246:       {
 247:         timer.stop();
 248:       }
 249: 
 250:       void decrement()
 251:       {
 252:         Object prev = BasicSpinnerUI.this.spinner.getPreviousValue();
 253:         if (prev != null)
 254:           BasicSpinnerUI.this.spinner.getModel().setValue(prev);
 255:       }
 256: 
 257:       volatile boolean mouseDown = false;
 258:       Timer timer = new Timer(50,
 259:                               new ActionListener()
 260:           {
 261:         public void actionPerformed(ActionEvent event)
 262:         {
 263:           decrement();
 264:         }
 265:           });
 266:         });
 267:   }
 268: 
 269:   /**
 270:    * Install this UI to the <code>JComponent</code>, which in reality, is a
 271:    * <code>JSpinner</code>. Calls <code>installDefaults</code>,
 272:    * <code>installListeners</code>, and also adds the buttons and editor.
 273:    *
 274:    * @param c DOCUMENT ME!
 275:    *
 276:    * @see #installDefaults
 277:    * @see #installListeners
 278:    * @see #createNextButton
 279:    * @see #createPreviousButton
 280:    * @see #createEditor
 281:    */
 282:   public void installUI(JComponent c)
 283:   {
 284:     super.installUI(c);
 285: 
 286:     spinner = (JSpinner) c;
 287: 
 288:     installDefaults();
 289:     installListeners();
 290: 
 291:     Component next = createNextButton();
 292:     Component previous = createPreviousButton();
 293: 
 294:     installNextButtonListeners(next);
 295:     installPreviousButtonListeners(previous);
 296: 
 297:     c.add(createEditor(), "Editor");
 298:     c.add(next, "Next");
 299:     c.add(previous, "Previous");
 300:   }
 301: 
 302:   /**
 303:    * Replace the old editor with the new one
 304:    *
 305:    * @param oldEditor the old editor
 306:    * @param newEditor the new one to replace with
 307:    */
 308:   protected void replaceEditor(JComponent oldEditor, JComponent newEditor)
 309:   {
 310:     spinner.remove(oldEditor);
 311:     spinner.add(newEditor);
 312:   }
 313: 
 314:   /**
 315:    * The reverse of <code>installDefaults</code>. Called by
 316:    * <code>uninstallUI</code>
 317:    */
 318:   protected void uninstallDefaults()
 319:   {
 320:     spinner.setLayout(null);
 321:   }
 322: 
 323:   /**
 324:    * The reverse of <code>installListeners</code>, called by
 325:    * <code>uninstallUI</code>
 326:    */
 327:   protected void uninstallListeners()
 328:   {
 329:     spinner.removePropertyChangeListener(listener);
 330:   }
 331: 
 332:   /**
 333:    * Called when the current L&F is replaced with another one, should call
 334:    * <code>uninstallDefaults</code> and <code>uninstallListeners</code> as
 335:    * well as remove the next/previous buttons and the editor
 336:    *
 337:    * @param c DOCUMENT ME!
 338:    */
 339:   public void uninstallUI(JComponent c)
 340:   {
 341:     super.uninstallUI(c);
 342: 
 343:     uninstallDefaults();
 344:     uninstallListeners();
 345:     c.removeAll();
 346:   }
 347: 
 348:   /** The spinner for this UI */
 349:   protected JSpinner spinner;
 350: 
 351:   /** DOCUMENT ME! */
 352:   private PropertyChangeListener listener = createPropertyChangeListener();
 353: 
 354:   /**
 355:    * DOCUMENT ME!
 356:    */
 357:   private class DefaultLayoutManager implements LayoutManager
 358:   {
 359:     /**
 360:      * DOCUMENT ME!
 361:      *
 362:      * @param parent DOCUMENT ME!
 363:      */
 364:     public void layoutContainer(Container parent)
 365:     {
 366:       synchronized (parent.getTreeLock())
 367:         {
 368:       Insets i = parent.getInsets();
 369:       boolean l2r = parent.getComponentOrientation().isLeftToRight();
 370:       /*
 371:         --------------    --------------
 372:         |        | n |    | n |        |
 373:         |   e    | - | or | - |   e    |
 374:         |        | p |    | p |        |
 375:         --------------    --------------
 376:       */
 377:       Dimension e = minSize(editor);
 378:       Dimension n = minSize(next);
 379:       Dimension p = minSize(previous);
 380:       Dimension s = spinner.getPreferredSize();
 381: 
 382:       int x = l2r ? i.left : i.right;
 383:       int y = i.top;
 384:       int w = Math.max(p.width, n.width);
 385:       int h = Math.max(p.height, n.height);
 386:       h = Math.max(h, e.height / 2);
 387:       int e_width = s.width - w;
 388: 
 389:       if (l2r)
 390:         {
 391:           setBounds(editor, x, y + (s.height - e.height) / 2, e_width,
 392:                     e.height);
 393:           x += e_width;
 394: 
 395:           setBounds(next, x, y, w, h);
 396:           y += h;
 397: 
 398:           setBounds(previous, x, y, w, h);
 399:         }
 400:       else
 401:         {
 402:           setBounds(next, x, y + (s.height - e.height) / 2, w, h);
 403:           y += h;
 404: 
 405:           setBounds(previous, x, y, w, h);
 406:           x += w;
 407:           y -= h;
 408: 
 409:           setBounds(editor, x, y, e_width, e.height);
 410:         }
 411:         }
 412:     }
 413: 
 414:     /**
 415:      * DOCUMENT ME!
 416:      *
 417:      * @param parent DOCUMENT ME!
 418:      *
 419:      * @return DOCUMENT ME!
 420:      */
 421:     public Dimension minimumLayoutSize(Container parent)
 422:     {
 423:       Dimension d = new Dimension();
 424: 
 425:       if (editor != null)
 426:         {
 427:       Dimension tmp = editor.getMinimumSize();
 428:       d.width += tmp.width;
 429:       d.height = tmp.height;
 430:         }
 431: 
 432:       int nextWidth = 0;
 433:       int previousWidth = 0;
 434:       int otherHeight = 0;
 435: 
 436:       if (next != null)
 437:         {
 438:       Dimension tmp = next.getMinimumSize();
 439:       nextWidth = tmp.width;
 440:       otherHeight += tmp.height;
 441:         }
 442:       if (previous != null)
 443:         {
 444:       Dimension tmp = previous.getMinimumSize();
 445:       previousWidth = tmp.width;
 446:       otherHeight += tmp.height;
 447:         }
 448: 
 449:       d.height = Math.max(d.height, otherHeight);
 450:       d.width += Math.max(nextWidth, previousWidth);
 451: 
 452:       return d;
 453:     }
 454: 
 455:     /**
 456:      * DOCUMENT ME!
 457:      *
 458:      * @param parent DOCUMENT ME!
 459:      *
 460:      * @return DOCUMENT ME!
 461:      */
 462:     public Dimension preferredLayoutSize(Container parent)
 463:     {
 464:       Dimension d = new Dimension();
 465: 
 466:       if (editor != null)
 467:         {
 468:       Dimension tmp = editor.getPreferredSize();
 469:       d.width += Math.max(tmp.width, 40);
 470:       d.height = tmp.height;
 471:         }
 472: 
 473:       int nextWidth = 0;
 474:       int previousWidth = 0;
 475:       int otherHeight = 0;
 476: 
 477:       if (next != null)
 478:         {
 479:       Dimension tmp = next.getPreferredSize();
 480:       nextWidth = tmp.width;
 481:       otherHeight += tmp.height;
 482:         }
 483:       if (previous != null)
 484:         {
 485:       Dimension tmp = previous.getPreferredSize();
 486:       previousWidth = tmp.width;
 487:       otherHeight += tmp.height;
 488:         }
 489: 
 490:       d.height = Math.max(d.height, otherHeight);
 491:       d.width += Math.max(nextWidth, previousWidth);
 492: 
 493:       return d;
 494:     }
 495: 
 496:     /**
 497:      * DOCUMENT ME!
 498:      *
 499:      * @param child DOCUMENT ME!
 500:      */
 501:     public void removeLayoutComponent(Component child)
 502:     {
 503:       if (child == editor)
 504:     editor = null;
 505:       else if (child == next)
 506:     next = null;
 507:       else if (previous == child)
 508:     previous = null;
 509:     }
 510: 
 511:     /**
 512:      * DOCUMENT ME!
 513:      *
 514:      * @param name DOCUMENT ME!
 515:      * @param child DOCUMENT ME!
 516:      */
 517:     public void addLayoutComponent(String name, Component child)
 518:     {
 519:       if ("Editor".equals(name))
 520:     editor = child;
 521:       else if ("Next".equals(name))
 522:     next = child;
 523:       else if ("Previous".equals(name))
 524:     previous = child;
 525:     }
 526: 
 527:     /**
 528:      * DOCUMENT ME!
 529:      *
 530:      * @param c DOCUMENT ME!
 531:      *
 532:      * @return DOCUMENT ME!
 533:      */
 534:     private Dimension minSize(Component c)
 535:     {
 536:       if (c == null)
 537:     return new Dimension();
 538:       else
 539:     return c.getMinimumSize();
 540:     }
 541: 
 542:     /**
 543:      * DOCUMENT ME!
 544:      *
 545:      * @param c DOCUMENT ME!
 546:      * @param x DOCUMENT ME!
 547:      * @param y DOCUMENT ME!
 548:      * @param w DOCUMENT ME!
 549:      * @param h DOCUMENT ME!
 550:      */
 551:     private void setBounds(Component c, int x, int y, int w, int h)
 552:     {
 553:       if (c != null)
 554:     c.setBounds(x, y, w, h);
 555:     }
 556: 
 557:     /** DOCUMENT ME! */
 558:     private Component editor;
 559: 
 560:     /** DOCUMENT ME! */
 561:     private Component next;
 562: 
 563:     /** DOCUMENT ME! */
 564:     private Component previous;
 565:   }
 566: }