001/* BasicDesktopIconUI.java --
002   Copyright (C) 2004, 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.basic;
040
041import java.awt.BorderLayout;
042import java.awt.Color;
043import java.awt.Component;
044import java.awt.Dimension;
045import java.awt.Graphics;
046import java.awt.Insets;
047import java.awt.Rectangle;
048import java.awt.event.ActionEvent;
049import java.awt.event.ActionListener;
050import java.awt.event.MouseEvent;
051import java.beans.PropertyChangeEvent;
052import java.beans.PropertyChangeListener;
053import java.beans.PropertyVetoException;
054
055import javax.swing.Icon;
056import javax.swing.JButton;
057import javax.swing.JComponent;
058import javax.swing.JDesktopPane;
059import javax.swing.JInternalFrame;
060import javax.swing.JInternalFrame.JDesktopIcon;
061import javax.swing.SwingConstants;
062import javax.swing.border.Border;
063import javax.swing.event.MouseInputAdapter;
064import javax.swing.event.MouseInputListener;
065import javax.swing.plaf.ComponentUI;
066import javax.swing.plaf.DesktopIconUI;
067
068/**
069 * This class acts as the UI delegate for JDesktopIcons for the Basic look and feel.
070 */
071public class BasicDesktopIconUI extends DesktopIconUI
072{
073  /**
074   * This helper class handles mouse events that occur on the JDesktopIcon.
075   */
076  public class MouseInputHandler extends MouseInputAdapter
077  {
078    /** The x offset from the MouseEvent coordinates to the top left corner. */
079    private transient int xOffset;
080
081    /** The y offset fromt he MouseEvent coordinates to the top left corner. */
082    private transient int yOffset;
083
084    /** A cached value of the JDesktopPane that parents this JDesktopIcon. */
085    private transient JDesktopPane pane;
086
087    /**
088     * This method is called when the mouse is dragged in the JDesktopIcon.
089     *
090     * @param e The MouseEvent.
091     */
092    public void mouseDragged(MouseEvent e)
093    {
094      Rectangle b = desktopIcon.getBounds();
095
096      moveAndRepaint(desktopIcon, b.x + e.getX() - xOffset,
097                     b.y + e.getY() - yOffset, b.width, b.height);
098    }
099
100    /**
101     * This method is called when the mouse is moved in the JDesktopIcon.
102     *
103     * @param e The MouseEvent.
104     */
105    public void mouseMoved(MouseEvent e)
106    {
107      // Nothing to do.
108    }
109
110    /**
111     * This method is called when the mouse is pressed in the JDesktopIcon.
112     *
113     * @param e The MouseEvent.
114     */
115    public void mousePressed(MouseEvent e)
116    {
117      xOffset = e.getX();
118      yOffset = e.getY();
119      pane = frame.getDesktopPane();
120      if (pane != null)
121        pane.getDesktopManager().beginDraggingFrame(desktopIcon);
122    }
123
124    /**
125     * This method is called when the mouse is released in the JDesktopIcon.
126     *
127     * @param e The MouseEvent.
128     */
129    public void mouseReleased(MouseEvent e)
130    {
131      if (pane != null)
132        pane.getDesktopManager().endDraggingFrame(desktopIcon);
133      xOffset = 0;
134      yOffset = 0;
135    }
136
137    /**
138     * This method moves and repaints the JDesktopIcon to the given bounds.
139     *
140     * @param f The JComponent to move and repaint.
141     * @param newX The new x coordinate.
142     * @param newY The new y coordinate.
143     * @param newWidth The new width.
144     * @param newHeight The new height.
145     */
146    public void moveAndRepaint(JComponent f, int newX, int newY, int newWidth,
147                               int newHeight)
148    {
149      if (pane != null)
150        pane.getDesktopManager().dragFrame(f, newX, newY);
151      else
152        desktopIcon.setBounds(newX, newY, newWidth, newHeight);
153    }
154  }
155
156  /**
157   * This class acts as the border for the JDesktopIcon.
158   */
159  private class DesktopIconBorder implements Border
160  {
161    /** The left inset value. */
162    int left = 10;
163
164    /** The top inset value. */
165    int top = 4;
166
167    /** The right inset value. */
168    int right = top;
169
170    /** The bottom inset value. */
171    int bottom = top;
172
173    /**
174     * This method returns the insets of the border.
175     *
176     * @param c The Component to find border insets for.
177     *
178     * @return The border insets.
179     */
180    public Insets getBorderInsets(Component c)
181    {
182      return new Insets(top, left, bottom, right);
183    }
184
185    /**
186     * This method returns whether the border is opaque.
187     *
188     * @return Whether the border is opaque.
189     */
190    public boolean isBorderOpaque()
191    {
192      return true;
193    }
194
195    /**
196     * This method paints the border.
197     *
198     * @param c The Component the border is in.
199     * @param g The Graphics object to paint with.
200     * @param x The x coordinate of the Component.
201     * @param y The y coordinate of the Component.
202     * @param width The width of the Component.
203     * @param height The height of the Component.
204     */
205    public void paintBorder(Component c, Graphics g, int x, int y, int width,
206                            int height)
207    {
208      g.translate(x, y);
209      Color saved = g.getColor();
210
211      g.setColor(Color.LIGHT_GRAY);
212
213      g.fillRect(0, 0, left, height);
214      g.fillRect(0, 0, width, top);
215      g.fillRect(0, height - bottom, width, bottom);
216      g.fillRect(width - right, 0, right, height);
217
218      g.setColor(Color.BLACK);
219      g.drawRect(0, 0, width - 1, height - 1);
220
221      int fHeight = height / 4;
222      int hLeft = left / 2;
223
224      g.setColor(Color.BLACK);
225      g.fillRect(hLeft, fHeight, 2, 2);
226      g.fillRect(hLeft, fHeight * 2, 2, 2);
227      g.fillRect(hLeft, fHeight * 3, 2, 2);
228
229      g.setColor(saved);
230      g.translate(-x, -y);
231    }
232  }
233
234  /** The static width and height of the iconSize. */
235  private static final int iconSize = 16;
236
237  /**
238   * This class represents the default frame icon when none
239   * is supplied by the JInternalFrame.
240   */
241  static class InternalFrameDefaultMenuIcon implements Icon
242  {
243    /**
244     * This returns the icon height.
245     *
246     * @return The icon height.
247     */
248    public int getIconHeight()
249    {
250      return iconSize;
251    }
252
253    /**
254     * This returns the icon width.
255     *
256     * @return The icon width.
257     */
258    public int getIconWidth()
259    {
260      return iconSize;
261    }
262
263    /**
264     * This method paints the icon.
265     *
266     * @param c The Component this icon belongs to.
267     * @param g The Graphics object to paint with.
268     * @param x The x coordinate to paint at.
269     * @param y The y coordinate to paint at.
270     */
271    public void paintIcon(Component c, Graphics g, int x, int y)
272    {
273      g.translate(x, y);
274      Color saved = g.getColor();
275
276      g.setColor(Color.BLUE);
277      g.fillRect(0, 0, iconSize, (int) ((double) iconSize / 3) + 1);
278
279      g.setColor(Color.WHITE);
280      g.fillRect(0, (int) ((double) iconSize / 3), iconSize, iconSize * 5 / 6);
281
282      g.setColor(Color.GRAY);
283      g.drawRect(0, 0, iconSize, iconSize);
284
285      g.setColor(saved);
286      g.translate(-x, -y);
287    }
288  }
289
290  /** The default JDesktopIcon width. */
291  private static final int iconWidth = 160;
292
293  /** The default JDesktopIcon height */
294  private static final int iconHeight = 35;
295
296  /** The JDesktopIcon this UI delegate represents. */
297  protected JDesktopIcon desktopIcon;
298
299  /** The JInternalFrame associated with the JDesktopIcon. */
300  protected JInternalFrame frame;
301
302  /** The MouseListener responsible for reacting to MouseEvents on the JDesktopIcon. */
303  private transient MouseInputListener mouseHandler;
304
305  /** The Button in the JDesktopIcon responsible for deiconifying it.
306   * This is package-private to avoid an accessor method. */
307  transient BoundButton button;
308
309  /** The PropertyChangeListener listening to the JDesktopIcon. */
310  private transient PropertyChangeListener propertyHandler;
311  
312  /** The default icon used when no frame icon is given by the JInternalFrame. */
313  static Icon defaultIcon = new InternalFrameDefaultMenuIcon();
314
315  /**
316   * This is a helper class that is used in JDesktopIcon and gives the Button a predetermined size.
317   */
318  private class BoundButton extends JButton
319  {
320    /**
321     * Creates a new BoundButton object.
322     *
323     * @param title The title of the button.
324     */
325    public BoundButton(String title)
326    {
327      super(title);
328    }
329
330    /**
331     * This method returns a standard size (based on the defaults of the JDesktopIcon) and the insets.
332     *
333     * @return The preferred size of the JDesktopIcon.
334     */
335    public Dimension getPreferredSize()
336    {
337      Insets insets = desktopIcon.getInsets();
338      return new Dimension(iconWidth - insets.left - insets.right,
339                           iconHeight - insets.top - insets.bottom);
340    }
341
342    /**
343     * This method returns the minimum size of the button.
344     *
345     * @return The minimum size of the button.
346     */
347    public Dimension getMinimumSize()
348    {
349      return getPreferredSize();
350    }
351
352    /**
353     * This method returns the maximum size of the button.
354     *
355     * @return The maximum size of the button.
356     */
357    public Dimension getMaximumSize()
358    {
359      return getPreferredSize();
360    }
361  }
362
363  /**
364   * Creates a new BasicDesktopIconUI object.
365   */
366  public BasicDesktopIconUI()
367  {
368    // Nothing to do here.
369  }
370
371  /**
372   * This method creates a new BasicDesktopIconUI for the given JComponent.
373   *
374   * @param c The JComponent to create a UI for.
375   *
376   * @return A new BasicDesktopIconUI.
377   */
378  public static ComponentUI createUI(JComponent c)
379  {
380    return new BasicDesktopIconUI();
381  }
382
383  /**
384   * This method installs the UI for the given JComponent.
385   *
386   * @param c The JComponent to install this UI for.
387   */
388  public void installUI(JComponent c)
389  {
390    if (c instanceof JDesktopIcon)
391      {
392        desktopIcon = (JDesktopIcon) c;
393        desktopIcon.setLayout(new BorderLayout());
394        frame = desktopIcon.getInternalFrame();
395
396        installDefaults();
397        installComponents();
398        installListeners();
399
400        desktopIcon.setOpaque(true);
401      }
402  }
403
404  /**
405   * This method uninstalls the UI for the given JComponent.
406   *
407   * @param c The JComponent to uninstall this UI for.
408   */
409  public void uninstallUI(JComponent c)
410  {
411    desktopIcon.setOpaque(false);
412    
413    uninstallListeners();
414    uninstallComponents();
415    uninstallDefaults();
416    
417    frame = null;
418    desktopIcon.setLayout(null);
419    desktopIcon = null;
420  }
421
422  /**
423   * This method installs the necessary sub components for the JDesktopIcon.
424   */
425  protected void installComponents()
426  {
427    // Try to create a button based on what the frame's
428    // state is currently
429    button = new BoundButton(frame.getTitle());
430    button.setHorizontalAlignment(SwingConstants.LEFT);
431    button.setHorizontalTextPosition(SwingConstants.TRAILING);
432
433    Icon use = frame.getFrameIcon();
434    if (use == null)
435      use = defaultIcon;
436    button.setIcon(use);
437
438    desktopIcon.add(button, SwingConstants.CENTER);
439  }
440
441  /**
442   * This method uninstalls the sub components for the JDesktopIcon.
443   */
444  protected void uninstallComponents()
445  {
446    desktopIcon.remove(button);
447    
448    button = null;
449  }
450
451  /**
452   * This method installs the listeners needed by this UI.
453   */
454  protected void installListeners()
455  {
456    mouseHandler = createMouseInputListener();
457
458    desktopIcon.addMouseMotionListener(mouseHandler);
459    desktopIcon.addMouseListener(mouseHandler);
460
461    propertyHandler = new PropertyChangeListener()
462        {
463          public void propertyChange(PropertyChangeEvent e)
464          {
465            if (e.getPropertyName().equals(JInternalFrame.TITLE_PROPERTY))
466              button.setText(desktopIcon.getInternalFrame().getTitle());
467            else if (e.getPropertyName().equals(JInternalFrame.FRAME_ICON_PROPERTY))
468              {
469                Icon use = desktopIcon.getInternalFrame().getFrameIcon();
470                if (use == null)
471                  use = defaultIcon;
472                button.setIcon(use);
473              }
474            desktopIcon.revalidate();
475            desktopIcon.repaint();
476          }
477        };
478    frame.addPropertyChangeListener(propertyHandler);
479
480    button.addActionListener(new ActionListener()
481        {
482          public void actionPerformed(ActionEvent e)
483          {
484            deiconize();
485          }
486        });
487  }
488
489  /**
490   * This method uninstalls the listeners needed by the UI.
491   */
492  protected void uninstallListeners()
493  {
494    // button is nulled so no need to remove it.
495    
496    frame.removePropertyChangeListener(propertyHandler);
497    propertyHandler = null;
498    
499    desktopIcon.removeMouseMotionListener(mouseHandler);
500    desktopIcon.removeMouseListener(mouseHandler);
501  }
502
503  /**
504   * This method installs the defaults for the JDesktopIcon.
505   */
506  protected void installDefaults()
507  {
508    // FIXME: Move border to defaults.
509    desktopIcon.setBorder(new DesktopIconBorder());  
510  }
511
512  /**
513   * This method uninstalls the defaults for the JDesktopIcon.
514   */
515  protected void uninstallDefaults()
516  {
517    desktopIcon.setBorder(null);
518  }
519
520  /**
521   * This method creates a new MouseInputListener for the JDesktopIcon.
522   *
523   * @return A new MouseInputListener.
524   */
525  protected MouseInputListener createMouseInputListener()
526  {
527    return new MouseInputHandler();
528  }
529
530  /**
531   * This method returns the preferred size for the given JComponent.
532   *
533   * @param c The JComponent to find a preferred size for.
534   *
535   * @return The preferred size.
536   */
537  public Dimension getPreferredSize(JComponent c)
538  {
539    return new Dimension(iconWidth, iconHeight);
540  }
541
542  /**
543   * This method returns the minimum size for the given JComponent.
544   *
545   * @param c The JComponent to find a minimum size for.
546   *
547   * @return The minimum size.
548   */
549  public Dimension getMinimumSize(JComponent c)
550  {
551    return getPreferredSize(c);
552  }
553
554  /**
555   * This method returns the maximum size for the given JComponent.
556   *
557   * @param c The JComponent to find a maximum size for.
558   *
559   * @return The maximum size.
560   */
561  public Dimension getMaximumSize(JComponent c)
562  {
563    return getPreferredSize(c);
564  }
565
566  /**
567   * This method returns the insets of the given JComponent.
568   *
569   * @param c The JComponent to find insets for.
570   *
571   * @return The insets of the given JComponent.
572   */
573  public Insets getInsets(JComponent c)
574  {
575    return c.getInsets();
576  }
577
578  /**
579   * This method deiconizes the JInternalFrame associated with the JDesktopIcon.
580   */
581  public void deiconize() 
582  {
583    try
584    {
585      frame.setIcon(false);
586    }
587    catch (PropertyVetoException pve)
588    {
589      // We do nothing if the attempt has been vetoed.
590    }
591  }
592}