1 /*
   2  * Copyright (c) 2002, 2014, 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     @SuppressWarnings("serial") // Same-version serialization only and
 198                                 // internal anonymous classes
 199     static class TaskBar extends JPanel implements ComponentListener, ContainerListener {
 200         TaskBar() {
 201             setOpaque(true);
 202             setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0) {
 203                 public void layoutContainer(Container target) {
 204                     // First shrink buttons to fit
 205                     Component[] comps = target.getComponents();
 206                     int n = comps.length;
 207                     if (n > 0) {
 208                         // Start with the largest preferred width
 209                         int prefWidth = 0;
 210                         for (Component c : comps) {
 211                             c.setPreferredSize(null);
 212                             Dimension prefSize = c.getPreferredSize();
 213                             if (prefSize.width > prefWidth) {
 214                                 prefWidth = prefSize.width;
 215                             }
 216                         }
 217                         // Shrink equally to fit if needed
 218                         Insets insets = target.getInsets();
 219                         int tw = target.getWidth() - insets.left - insets.right;
 220                         int w = Math.min(prefWidth, Math.max(10, tw/n));
 221                         for (Component c : comps) {
 222                             Dimension prefSize = c.getPreferredSize();
 223                             c.setPreferredSize(new Dimension(w, prefSize.height));
 224                         }
 225                     }
 226                     super.layoutContainer(target);
 227                 }
 228             });
 229 
 230             // PENDING: This should be handled by the painter
 231             setBorder(new BevelBorder(BevelBorder.RAISED) {
 232                 protected void paintRaisedBevel(Component c, Graphics g,
 233                                                 int x, int y, int w, int h)  {
 234                     Color oldColor = g.getColor();
 235                     g.translate(x, y);
 236                     g.setColor(getHighlightOuterColor(c));
 237                     g.drawLine(0, 0, 0, h-2);
 238                     g.drawLine(1, 0, w-2, 0);
 239                     g.setColor(getShadowOuterColor(c));
 240                     g.drawLine(0, h-1, w-1, h-1);
 241                     g.drawLine(w-1, 0, w-1, h-2);
 242                     g.translate(-x, -y);
 243                     g.setColor(oldColor);
 244                 }
 245             });
 246         }
 247 
 248         void adjustSize() {
 249             JDesktopPane desktop = (JDesktopPane)getParent();
 250             if (desktop != null) {
 251                 int height = getPreferredSize().height;
 252                 Insets insets = getInsets();
 253                 if (height == insets.top + insets.bottom) {
 254                     if (getHeight() <= height) {
 255                         // Initial size, because we have no buttons yet
 256                         height += 21;
 257                     } else {
 258                         // We already have a good height
 259                         height = getHeight();
 260                     }
 261                 }
 262                 setBounds(0, desktop.getHeight() - height, desktop.getWidth(), height);
 263                 revalidate();
 264                 repaint();
 265             }
 266         }
 267 
 268         // ComponentListener interface
 269 
 270         public void componentResized(ComponentEvent e) {
 271             if (e.getSource() instanceof JDesktopPane) {
 272                 adjustSize();
 273             }
 274         }
 275 
 276         public void componentMoved(ComponentEvent e){}
 277 
 278         public void componentShown(ComponentEvent e) {
 279             if (e.getSource() instanceof JInternalFrame) {
 280                 adjustSize();
 281             }
 282         }
 283 
 284         public void componentHidden(ComponentEvent e) {
 285             if (e.getSource() instanceof JInternalFrame) {
 286                 ((JInternalFrame)e.getSource()).getDesktopIcon().setVisible(false);
 287                 revalidate();
 288             }
 289         }
 290 
 291         // ContainerListener interface
 292 
 293         public void componentAdded(ContainerEvent e) {
 294             if (e.getChild() instanceof JInternalFrame) {
 295                 JDesktopPane desktop = (JDesktopPane)e.getSource();
 296                 JInternalFrame f = (JInternalFrame)e.getChild();
 297                 JInternalFrame.JDesktopIcon desktopIcon = f.getDesktopIcon();
 298                 for (Component comp : getComponents()) {
 299                     if (comp == desktopIcon) {
 300                         // We have it already
 301                         return;
 302                     }
 303                 }
 304                 add(desktopIcon);
 305                 f.addComponentListener(this);
 306                 if (getComponentCount() == 1) {
 307                     adjustSize();
 308                 }
 309             }
 310         }
 311 
 312         public void componentRemoved(ContainerEvent e) {
 313             if (e.getChild() instanceof JInternalFrame) {
 314                 JInternalFrame f = (JInternalFrame)e.getChild();
 315                 if (!f.isIcon()) {
 316                     // Frame was removed without using setClosed(true)
 317                     remove(f.getDesktopIcon());
 318                     f.removeComponentListener(this);
 319                     revalidate();
 320                     repaint();
 321                 }
 322             }
 323         }
 324     }
 325 
 326     @SuppressWarnings("serial") // Same-version serialization only
 327     class SynthDesktopManager extends DefaultDesktopManager implements UIResource {
 328 
 329         public void maximizeFrame(JInternalFrame f) {
 330             if (f.isIcon()) {
 331                 try {
 332                     f.setIcon(false);
 333                 } catch (PropertyVetoException e2) {
 334                 }
 335             } else {
 336                 f.setNormalBounds(f.getBounds());
 337                 Component desktop = f.getParent();
 338                 setBoundsForFrame(f, 0, 0,
 339                                   desktop.getWidth(),
 340                                   desktop.getHeight() - taskBar.getHeight());
 341             }
 342 
 343             try {
 344                 f.setSelected(true);
 345             } catch (PropertyVetoException e2) {
 346             }
 347         }
 348 
 349         public void iconifyFrame(JInternalFrame f) {
 350             JInternalFrame.JDesktopIcon desktopIcon;
 351             Container c = f.getParent();
 352             JDesktopPane d = f.getDesktopPane();
 353             boolean findNext = f.isSelected();
 354 
 355             if (c == null) {
 356                 return;
 357             }
 358 
 359             desktopIcon = f.getDesktopIcon();
 360 
 361             if (!f.isMaximum()) {
 362                 f.setNormalBounds(f.getBounds());
 363             }
 364             c.remove(f);
 365             c.repaint(f.getX(), f.getY(), f.getWidth(), f.getHeight());
 366             try {
 367                 f.setSelected(false);
 368             } catch (PropertyVetoException e2) {
 369             }
 370 
 371             // Get topmost of the remaining frames
 372             if (findNext) {
 373                 for (Component comp : c.getComponents()) {
 374                     if (comp instanceof JInternalFrame) {
 375                         try {
 376                             ((JInternalFrame)comp).setSelected(true);
 377                         } catch (PropertyVetoException e2) {
 378                         }
 379                         ((JInternalFrame)comp).moveToFront();
 380                         return;
 381                     }
 382                 }
 383             }
 384         }
 385 
 386 
 387         public void deiconifyFrame(JInternalFrame f) {
 388             JInternalFrame.JDesktopIcon desktopIcon = f.getDesktopIcon();
 389             Container c = desktopIcon.getParent();
 390             if (c != null) {
 391                 c = c.getParent();
 392                 if (c != null) {
 393                     c.add(f);
 394                     if (f.isMaximum()) {
 395                         int w = c.getWidth();
 396                         int h = c.getHeight() - taskBar.getHeight();
 397                         if (f.getWidth() != w || f.getHeight() != h) {
 398                             setBoundsForFrame(f, 0, 0, w, h);
 399                         }
 400                     }
 401                     if (f.isSelected()) {
 402                         f.moveToFront();
 403                     } else {
 404                         try {
 405                             f.setSelected(true);
 406                         } catch (PropertyVetoException e2) {
 407                         }
 408                     }
 409                 }
 410             }
 411         }
 412 
 413         protected void removeIconFor(JInternalFrame f) {
 414             super.removeIconFor(f);
 415             taskBar.validate();
 416         }
 417 
 418         public void setBoundsForFrame(JComponent f, int newX, int newY, int newWidth, int newHeight) {
 419             super.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
 420             if (taskBar != null && newY >= taskBar.getY()) {
 421                 f.setLocation(f.getX(), taskBar.getY()-f.getInsets().top);
 422             }
 423         }
 424     }
 425 
 426     /**
 427      * {@inheritDoc}
 428      */
 429     @Override
 430     public SynthContext getContext(JComponent c) {
 431         return getContext(c, getComponentState(c));
 432     }
 433 
 434     private SynthContext getContext(JComponent c, int state) {
 435         return SynthContext.getContext(SynthContext.class, c,
 436                      SynthLookAndFeel.getRegion(c), style, state);
 437     }
 438 
 439     private int getComponentState(JComponent c) {
 440         return SynthLookAndFeel.getComponentState(c);
 441     }
 442 
 443     /**
 444      * Notifies this UI delegate to repaint the specified component.
 445      * This method paints the component background, then calls
 446      * the {@link #paint(SynthContext,Graphics)} method.
 447      *
 448      * <p>In general, this method does not need to be overridden by subclasses.
 449      * All Look and Feel rendering code should reside in the {@code paint} method.
 450      *
 451      * @param g the {@code Graphics} object used for painting
 452      * @param c the component being painted
 453      * @see #paint(SynthContext,Graphics)
 454      */
 455     @Override
 456     public void update(Graphics g, JComponent c) {
 457         SynthContext context = getContext(c);
 458 
 459         SynthLookAndFeel.update(context, g);
 460         context.getPainter().paintDesktopPaneBackground(context, g, 0, 0,
 461                                                   c.getWidth(), c.getHeight());
 462         paint(context, g);
 463         context.dispose();
 464     }
 465 
 466     /**
 467      * Paints the specified component according to the Look and Feel.
 468      * <p>This method is not used by Synth Look and Feel.
 469      * Painting is handled by the {@link #paint(SynthContext,Graphics)} method.
 470      *
 471      * @param g the {@code Graphics} object used for painting
 472      * @param c the component being painted
 473      * @see #paint(SynthContext,Graphics)
 474      */
 475     @Override
 476     public void paint(Graphics g, JComponent c) {
 477         SynthContext context = getContext(c);
 478 
 479         paint(context, g);
 480         context.dispose();
 481     }
 482 
 483     /**
 484      * Paints the specified component. This implementation does nothing.
 485      *
 486      * @param context context for the component being painted
 487      * @param g the {@code Graphics} object used for painting
 488      * @see #update(Graphics,JComponent)
 489      */
 490     protected void paint(SynthContext context, Graphics g) {
 491     }
 492 
 493     /**
 494      * {@inheritDoc}
 495      */
 496     @Override
 497     public void paintBorder(SynthContext context, Graphics g, int x,
 498                             int y, int w, int h) {
 499         context.getPainter().paintDesktopPaneBorder(context, g, x, y, w, h);
 500     }
 501 
 502     /**
 503      * {@inheritDoc}
 504      */
 505     @Override
 506     public void propertyChange(PropertyChangeEvent evt) {
 507         if (SynthLookAndFeel.shouldUpdateStyle(evt)) {
 508             updateStyle((JDesktopPane)evt.getSource());
 509         }
 510         if (evt.getPropertyName() == "ancestor" && taskBar != null) {
 511             taskBar.adjustSize();
 512         }
 513     }
 514 }