1 /*
   2  * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.swing.plaf.synth;
  27 
  28 import javax.swing.*;
  29 import javax.swing.border.*;
  30 import javax.swing.plaf.*;
  31 import javax.swing.plaf.basic.BasicDesktopPaneUI;
  32 import java.beans.*;
  33 import java.awt.event.*;
  34 import java.awt.*;
  35 
  36 /**
  37  * Provides the Synth L&F UI delegate for
  38  * {@link javax.swing.JDesktopPane}.
  39  *
  40  * @author Joshua Outwater
  41  * @author Steve Wilson
  42  * @since 1.7
  43  */
  44 public class SynthDesktopPaneUI extends BasicDesktopPaneUI implements
  45                   PropertyChangeListener, SynthUI {
  46     private SynthStyle style;
  47     private TaskBar taskBar;
  48     private DesktopManager oldDesktopManager;
  49 
  50     /**
  51      * Creates a new UI object for the given component.
  52      *
  53      * @param c component to create UI object for
  54      * @return the UI object
  55      */
  56     public static ComponentUI createUI(JComponent c) {
  57         return new SynthDesktopPaneUI();
  58     }
  59 
  60     /**
  61      * @inheritDoc
  62      */
  63     @Override
  64     protected void installListeners() {
  65         super.installListeners();
  66         desktop.addPropertyChangeListener(this);
  67         if (taskBar != null) {
  68             // Listen for desktop being resized
  69             desktop.addComponentListener(taskBar);
  70             // Listen for frames being added to desktop
  71             desktop.addContainerListener(taskBar);
  72         }
  73     }
  74 
  75     /**
  76      * @inheritDoc
  77      */
  78     @Override
  79     protected void installDefaults() {
  80         updateStyle(desktop);
  81 
  82         if (UIManager.getBoolean("InternalFrame.useTaskBar")) {
  83             taskBar = new TaskBar();
  84 
  85             for (Component comp : desktop.getComponents()) {
  86                 JInternalFrame.JDesktopIcon desktopIcon;
  87 
  88                 if (comp instanceof JInternalFrame.JDesktopIcon) {
  89                     desktopIcon = (JInternalFrame.JDesktopIcon)comp;
  90                 } else if (comp instanceof JInternalFrame) {
  91                     desktopIcon = ((JInternalFrame)comp).getDesktopIcon();
  92                 } else {
  93                     continue;
  94                 }
  95                 // Move desktopIcon from desktop to taskBar
  96                 if (desktopIcon.getParent() == desktop) {
  97                     desktop.remove(desktopIcon);
  98                 }
  99                 if (desktopIcon.getParent() != taskBar) {
 100                     taskBar.add(desktopIcon);
 101                     desktopIcon.getInternalFrame().addComponentListener(
 102                         taskBar);
 103                 }
 104             }
 105             taskBar.setBackground(desktop.getBackground());
 106             desktop.add(taskBar,
 107                 Integer.valueOf(JLayeredPane.PALETTE_LAYER.intValue() + 1));
 108             if (desktop.isShowing()) {
 109                 taskBar.adjustSize();
 110             }
 111         }
 112     }
 113 
 114     private void updateStyle(JDesktopPane c) {
 115         SynthStyle oldStyle = style;
 116         SynthContext context = getContext(c, ENABLED);
 117         style = SynthLookAndFeel.updateStyle(context, this);
 118         if (oldStyle != null) {
 119             uninstallKeyboardActions();
 120             installKeyboardActions();
 121         }
 122         context.dispose();
 123     }
 124 
 125     /**
 126      * @inheritDoc
 127      */
 128     @Override
 129     protected void uninstallListeners() {
 130         if (taskBar != null) {
 131             desktop.removeComponentListener(taskBar);
 132             desktop.removeContainerListener(taskBar);
 133         }
 134         desktop.removePropertyChangeListener(this);
 135         super.uninstallListeners();
 136     }
 137 
 138     /**
 139      * @inheritDoc
 140      */
 141     @Override
 142     protected void uninstallDefaults() {
 143         SynthContext context = getContext(desktop, ENABLED);
 144 
 145         style.uninstallDefaults(context);
 146         context.dispose();
 147         style = null;
 148 
 149         if (taskBar != null) {
 150             for (Component comp : taskBar.getComponents()) {
 151                 JInternalFrame.JDesktopIcon desktopIcon =
 152                     (JInternalFrame.JDesktopIcon)comp;
 153                 taskBar.remove(desktopIcon);
 154                 desktopIcon.setPreferredSize(null);
 155                 JInternalFrame f = desktopIcon.getInternalFrame();
 156                 if (f.isIcon()) {
 157                     desktop.add(desktopIcon);
 158                 }
 159                 f.removeComponentListener(taskBar);
 160             }
 161             desktop.remove(taskBar);
 162             taskBar = null;
 163         }
 164     }
 165 
 166     /**
 167      * @inheritDoc
 168      */
 169     @Override
 170     protected void installDesktopManager() {
 171         if (UIManager.getBoolean("InternalFrame.useTaskBar")) {
 172             desktopManager = oldDesktopManager = desktop.getDesktopManager();
 173             if (!(desktopManager instanceof SynthDesktopManager)) {
 174                 desktopManager = new SynthDesktopManager();
 175                 desktop.setDesktopManager(desktopManager);
 176             }
 177         } else {
 178             super.installDesktopManager();
 179         }
 180     }
 181 
 182     /**
 183      * @inheritDoc
 184      */
 185     @Override
 186     protected void uninstallDesktopManager() {
 187         if (oldDesktopManager != null && !(oldDesktopManager instanceof UIResource)) {
 188             desktopManager = desktop.getDesktopManager();
 189             if (desktopManager == null || desktopManager instanceof UIResource) {
 190                 desktop.setDesktopManager(oldDesktopManager);
 191             }
 192         }
 193         oldDesktopManager = null;
 194         super.uninstallDesktopManager();
 195     }
 196 
 197     static class TaskBar extends JPanel implements ComponentListener, ContainerListener {
 198         TaskBar() {
 199             setOpaque(true);
 200             setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0) {
 201                 public void layoutContainer(Container target) {
 202                     // First shrink buttons to fit
 203                     Component[] comps = target.getComponents();
 204                     int n = comps.length;
 205                     if (n > 0) {
 206                         // Start with the largest preferred width
 207                         int prefWidth = 0;
 208                         for (Component c : comps) {
 209                             c.setPreferredSize(null);
 210                             Dimension prefSize = c.getPreferredSize();
 211                             if (prefSize.width > prefWidth) {
 212                                 prefWidth = prefSize.width;
 213                             }
 214                         }
 215                         // Shrink equally to fit if needed
 216                         Insets insets = target.getInsets();
 217                         int tw = target.getWidth() - insets.left - insets.right;
 218                         int w = Math.min(prefWidth, Math.max(10, tw/n));
 219                         for (Component c : comps) {
 220                             Dimension prefSize = c.getPreferredSize();
 221                             c.setPreferredSize(new Dimension(w, prefSize.height));
 222                         }
 223                     }
 224                     super.layoutContainer(target);
 225                 }
 226             });
 227 
 228             // PENDING: This should be handled by the painter
 229             setBorder(new BevelBorder(BevelBorder.RAISED) {
 230                 protected void paintRaisedBevel(Component c, Graphics g,
 231                                                 int x, int y, int w, int h)  {
 232                     Color oldColor = g.getColor();
 233                     g.translate(x, y);
 234                     g.setColor(getHighlightOuterColor(c));
 235                     g.drawLine(0, 0, 0, h-2);
 236                     g.drawLine(1, 0, w-2, 0);
 237                     g.setColor(getShadowOuterColor(c));
 238                     g.drawLine(0, h-1, w-1, h-1);
 239                     g.drawLine(w-1, 0, w-1, h-2);
 240                     g.translate(-x, -y);
 241                     g.setColor(oldColor);
 242                 }
 243             });
 244         }
 245 
 246         void adjustSize() {
 247             JDesktopPane desktop = (JDesktopPane)getParent();
 248             if (desktop != null) {
 249                 int height = getPreferredSize().height;
 250                 Insets insets = getInsets();
 251                 if (height == insets.top + insets.bottom) {
 252                     if (getHeight() <= height) {
 253                         // Initial size, because we have no buttons yet
 254                         height += 21;
 255                     } else {
 256                         // We already have a good height
 257                         height = getHeight();
 258                     }
 259                 }
 260                 setBounds(0, desktop.getHeight() - height, desktop.getWidth(), height);
 261                 revalidate();
 262                 repaint();
 263             }
 264         }
 265 
 266         // ComponentListener interface
 267 
 268         public void componentResized(ComponentEvent e) {
 269             if (e.getSource() instanceof JDesktopPane) {
 270                 adjustSize();
 271             }
 272         }
 273 
 274         public void componentMoved(ComponentEvent e){}
 275 
 276         public void componentShown(ComponentEvent e) {
 277             if (e.getSource() instanceof JInternalFrame) {
 278                 adjustSize();
 279             }
 280         }
 281 
 282         public void componentHidden(ComponentEvent e) {
 283             if (e.getSource() instanceof JInternalFrame) {
 284                 ((JInternalFrame)e.getSource()).getDesktopIcon().setVisible(false);
 285                 revalidate();
 286             }
 287         }
 288 
 289         // ContainerListener interface
 290 
 291         public void componentAdded(ContainerEvent e) {
 292             if (e.getChild() instanceof JInternalFrame) {
 293                 JDesktopPane desktop = (JDesktopPane)e.getSource();
 294                 JInternalFrame f = (JInternalFrame)e.getChild();
 295                 JInternalFrame.JDesktopIcon desktopIcon = f.getDesktopIcon();
 296                 for (Component comp : getComponents()) {
 297                     if (comp == desktopIcon) {
 298                         // We have it already
 299                         return;
 300                     }
 301                 }
 302                 add(desktopIcon);
 303                 f.addComponentListener(this);
 304                 if (getComponentCount() == 1) {
 305                     adjustSize();
 306                 }
 307             }
 308         }
 309 
 310         public void componentRemoved(ContainerEvent e) {
 311             if (e.getChild() instanceof JInternalFrame) {
 312                 JInternalFrame f = (JInternalFrame)e.getChild();
 313                 if (!f.isIcon()) {
 314                     // Frame was removed without using setClosed(true)
 315                     remove(f.getDesktopIcon());
 316                     f.removeComponentListener(this);
 317                     revalidate();
 318                     repaint();
 319                 }
 320             }
 321         }
 322     }
 323 
 324 
 325     class SynthDesktopManager extends DefaultDesktopManager implements UIResource {
 326 
 327         public void maximizeFrame(JInternalFrame f) {
 328             if (f.isIcon()) {
 329                 try {
 330                     f.setIcon(false);
 331                 } catch (PropertyVetoException e2) {
 332                 }
 333             } else {
 334                 f.setNormalBounds(f.getBounds());
 335                 Component desktop = f.getParent();
 336                 setBoundsForFrame(f, 0, 0,
 337                                   desktop.getWidth(),
 338                                   desktop.getHeight() - taskBar.getHeight());
 339             }
 340 
 341             try {
 342                 f.setSelected(true);
 343             } catch (PropertyVetoException e2) {
 344             }
 345         }
 346 
 347         public void iconifyFrame(JInternalFrame f) {
 348             JInternalFrame.JDesktopIcon desktopIcon;
 349             Container c = f.getParent();
 350             JDesktopPane d = f.getDesktopPane();
 351             boolean findNext = f.isSelected();
 352 
 353             if (c == null) {
 354                 return;
 355             }
 356 
 357             desktopIcon = f.getDesktopIcon();
 358 
 359             if (!f.isMaximum()) {
 360                 f.setNormalBounds(f.getBounds());
 361             }
 362             c.remove(f);
 363             c.repaint(f.getX(), f.getY(), f.getWidth(), f.getHeight());
 364             try {
 365                 f.setSelected(false);
 366             } catch (PropertyVetoException e2) {
 367             }
 368 
 369             // Get topmost of the remaining frames
 370             if (findNext) {
 371                 for (Component comp : c.getComponents()) {
 372                     if (comp instanceof JInternalFrame) {
 373                         try {
 374                             ((JInternalFrame)comp).setSelected(true);
 375                         } catch (PropertyVetoException e2) {
 376                         }
 377                         ((JInternalFrame)comp).moveToFront();
 378                         return;
 379                     }
 380                 }
 381             }
 382         }
 383 
 384 
 385         public void deiconifyFrame(JInternalFrame f) {
 386             JInternalFrame.JDesktopIcon desktopIcon = f.getDesktopIcon();
 387             Container c = desktopIcon.getParent();
 388             if (c != null) {
 389                 c = c.getParent();
 390                 if (c != null) {
 391                     c.add(f);
 392                     if (f.isMaximum()) {
 393                         int w = c.getWidth();
 394                         int h = c.getHeight() - taskBar.getHeight();
 395                         if (f.getWidth() != w || f.getHeight() != h) {
 396                             setBoundsForFrame(f, 0, 0, w, h);
 397                         }
 398                     }
 399                     if (f.isSelected()) {
 400                         f.moveToFront();
 401                     } else {
 402                         try {
 403                             f.setSelected(true);
 404                         } catch (PropertyVetoException e2) {
 405                         }
 406                     }
 407                 }
 408             }
 409         }
 410 
 411         protected void removeIconFor(JInternalFrame f) {
 412             super.removeIconFor(f);
 413             taskBar.validate();
 414         }
 415 
 416         public void setBoundsForFrame(JComponent f, int newX, int newY, int newWidth, int newHeight) {
 417             super.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
 418             if (taskBar != null && newY >= taskBar.getY()) {
 419                 f.setLocation(f.getX(), taskBar.getY()-f.getInsets().top);
 420             }
 421         }
 422     }
 423 
 424     /**
 425      * @inheritDoc
 426      */
 427     @Override
 428     public SynthContext getContext(JComponent c) {
 429         return getContext(c, getComponentState(c));
 430     }
 431 
 432     private SynthContext getContext(JComponent c, int state) {
 433         return SynthContext.getContext(SynthContext.class, c,
 434                      SynthLookAndFeel.getRegion(c), style, state);
 435     }
 436 
 437     private int getComponentState(JComponent c) {
 438         return SynthLookAndFeel.getComponentState(c);
 439     }
 440 
 441     /**
 442      * Notifies this UI delegate to repaint the specified component.
 443      * This method paints the component background, then calls
 444      * the {@link #paint(SynthContext,Graphics)} method.
 445      *
 446      * <p>In general, this method does not need to be overridden by subclasses.
 447      * All Look and Feel rendering code should reside in the {@code paint} method.
 448      *
 449      * @param g the {@code Graphics} object used for painting
 450      * @param c the component being painted
 451      * @see #paint(SynthContext,Graphics)
 452      */
 453     @Override
 454     public void update(Graphics g, JComponent c) {
 455         SynthContext context = getContext(c);
 456 
 457         SynthLookAndFeel.update(context, g);
 458         context.getPainter().paintDesktopPaneBackground(context, g, 0, 0,
 459                                                   c.getWidth(), c.getHeight());
 460         paint(context, g);
 461         context.dispose();
 462     }
 463 
 464     /**
 465      * Paints the specified component according to the Look and Feel.
 466      * <p>This method is not used by Synth Look and Feel.
 467      * Painting is handled by the {@link #paint(SynthContext,Graphics)} method.
 468      *
 469      * @param g the {@code Graphics} object used for painting
 470      * @param c the component being painted
 471      * @see #paint(SynthContext,Graphics)
 472      */
 473     @Override
 474     public void paint(Graphics g, JComponent c) {
 475         SynthContext context = getContext(c);
 476 
 477         paint(context, g);
 478         context.dispose();
 479     }
 480 
 481     /**
 482      * Paints the specified component. This implementation does nothing.
 483      *
 484      * @param context context for the component being painted
 485      * @param g the {@code Graphics} object used for painting
 486      * @see #update(Graphics,JComponent)
 487      */
 488     protected void paint(SynthContext context, Graphics g) {
 489     }
 490 
 491     /**
 492      * @inheritDoc
 493      */
 494     @Override
 495     public void paintBorder(SynthContext context, Graphics g, int x,
 496                             int y, int w, int h) {
 497         context.getPainter().paintDesktopPaneBorder(context, g, x, y, w, h);
 498     }
 499 
 500     /**
 501      * @inheritDoc
 502      */
 503     @Override
 504     public void propertyChange(PropertyChangeEvent evt) {
 505         if (SynthLookAndFeel.shouldUpdateStyle(evt)) {
 506             updateStyle((JDesktopPane)evt.getSource());
 507         }
 508         if (evt.getPropertyName() == "ancestor" && taskBar != null) {
 509             taskBar.adjustSize();
 510         }
 511     }
 512 }