001    /* BasicDesktopIconUI.java --
002       Copyright (C) 2004, 2005 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.swing.plaf.basic;
040    
041    import java.awt.BorderLayout;
042    import java.awt.Color;
043    import java.awt.Component;
044    import java.awt.Dimension;
045    import java.awt.Graphics;
046    import java.awt.Insets;
047    import java.awt.Rectangle;
048    import java.awt.event.ActionEvent;
049    import java.awt.event.ActionListener;
050    import java.awt.event.MouseEvent;
051    import java.beans.PropertyChangeEvent;
052    import java.beans.PropertyChangeListener;
053    import java.beans.PropertyVetoException;
054    
055    import javax.swing.Icon;
056    import javax.swing.JButton;
057    import javax.swing.JComponent;
058    import javax.swing.JDesktopPane;
059    import javax.swing.JInternalFrame;
060    import javax.swing.JInternalFrame.JDesktopIcon;
061    import javax.swing.SwingConstants;
062    import javax.swing.border.Border;
063    import javax.swing.event.MouseInputAdapter;
064    import javax.swing.event.MouseInputListener;
065    import javax.swing.plaf.ComponentUI;
066    import javax.swing.plaf.DesktopIconUI;
067    
068    /**
069     * This class acts as the UI delegate for JDesktopIcons for the Basic look and feel.
070     */
071    public 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    }