1 /*
   2  * Copyright (c) 2011, 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 com.apple.laf;
  27 
  28 import java.awt.*;
  29 import java.beans.PropertyVetoException;
  30 import java.util.Vector;
  31 
  32 import javax.swing.*;
  33 
  34 /**
  35  * Based on AquaInternalFrameManager
  36  *
  37  * DesktopManager implementation for Aqua
  38  *
  39  * Mac is more like Windows than it's like Motif/Basic
  40  *
  41  *    From WindowsDesktopManager:
  42  *
  43  * This class implements a DesktopManager which more closely follows
  44  * the MDI model than the DefaultDesktopManager.  Unlike the
  45  * DefaultDesktopManager policy, MDI requires that the selected
  46  * and activated child frames are the same, and that that frame
  47  * always be the top-most window.
  48  * <p>
  49  * The maximized state is managed by the DesktopManager with MDI,
  50  * instead of just being a property of the individual child frame.
  51  * This means that if the currently selected window is maximized
  52  * and another window is selected, that new window will be maximized.
  53  *
  54  * @see com.sun.java.swing.plaf.windows.WindowsDesktopManager
  55  */
  56 public class AquaInternalFrameManager extends DefaultDesktopManager {
  57     // Variables
  58 
  59     /* The frame which is currently selected/activated.
  60      * We store this value to enforce Mac's single-selection model.
  61      */
  62     JInternalFrame fCurrentFrame;
  63     JInternalFrame fInitialFrame;
  64     AquaInternalFramePaneUI fCurrentPaneUI;
  65 
  66     /* The list of frames, sorted by order of creation.
  67      * This list is necessary because by default the order of
  68      * child frames in the JDesktopPane changes during frame
  69      * activation (the activated frame is moved to index 0).
  70      * We preserve the creation order so that "next" and "previous"
  71      * frame actions make sense.
  72      */
  73     Vector<JInternalFrame> fChildFrames = new Vector<JInternalFrame>(1);
  74 
  75     public void closeFrame(final JInternalFrame f) {
  76         if (f == fCurrentFrame) {
  77             activateNextFrame();
  78         }
  79         fChildFrames.removeElement(f);
  80         super.closeFrame(f);
  81     }
  82 
  83     public void deiconifyFrame(final JInternalFrame f) {
  84         JInternalFrame.JDesktopIcon desktopIcon;
  85 
  86         desktopIcon = f.getDesktopIcon();
  87         // If the icon moved, move the frame to that spot before expanding it
  88         // reshape does delta checks for us
  89         f.reshape(desktopIcon.getX(), desktopIcon.getY(), f.getWidth(), f.getHeight());
  90         super.deiconifyFrame(f);
  91     }
  92 
  93     void addIcon(final Container c, final JInternalFrame.JDesktopIcon desktopIcon) {
  94         c.add(desktopIcon);
  95     }
  96 
  97     /** Removes the frame from its parent and adds its desktopIcon to the parent. */
  98     public void iconifyFrame(final JInternalFrame f) {
  99         // Same as super except doesn't deactivate it
 100         JInternalFrame.JDesktopIcon desktopIcon;
 101         Container c;
 102 
 103         desktopIcon = f.getDesktopIcon();
 104         // Position depends on *current* position of frame, unlike super which reuses the first position
 105         final Rectangle r = getBoundsForIconOf(f);
 106         desktopIcon.setBounds(r.x, r.y, r.width, r.height);
 107 
 108         c = f.getParent();
 109         if (c == null) return;
 110 
 111         c.remove(f);
 112         addIcon(c, desktopIcon);
 113         c.repaint(f.getX(), f.getY(), f.getWidth(), f.getHeight());
 114     }
 115 
 116     // WindowsDesktopManager code
 117     public void activateFrame(final JInternalFrame f) {
 118         try {
 119             if (f != null) super.activateFrame(f);
 120 
 121             // If this is the first activation, add to child list.
 122             if (fChildFrames.indexOf(f) == -1) {
 123                 fChildFrames.addElement(f);
 124             }
 125 
 126             if (fCurrentFrame != null && f != fCurrentFrame) {
 127                 if (fCurrentFrame.isSelected()) {
 128                     fCurrentFrame.setSelected(false);
 129                 }
 130             }
 131 
 132             if (f != null && !f.isSelected()) {
 133                 f.setSelected(true);
 134             }
 135 
 136             fCurrentFrame = f;
 137         } catch(final PropertyVetoException e) {}
 138     }
 139 
 140     private void switchFrame(final boolean next) {
 141         if (fCurrentFrame == null) {
 142             // initialize first frame we find
 143             if (fInitialFrame != null) activateFrame(fInitialFrame);
 144             return;
 145         }
 146 
 147         final int count = fChildFrames.size();
 148         if (count <= 1) {
 149             // No other child frames.
 150             return;
 151         }
 152 
 153         final int currentIndex = fChildFrames.indexOf(fCurrentFrame);
 154         if (currentIndex == -1) {
 155             // the "current frame" is no longer in the list
 156             fCurrentFrame = null;
 157             return;
 158         }
 159 
 160         int nextIndex;
 161         if (next) {
 162             nextIndex = currentIndex + 1;
 163             if (nextIndex == count) {
 164                 nextIndex = 0;
 165             }
 166         } else {
 167             nextIndex = currentIndex - 1;
 168             if (nextIndex == -1) {
 169                 nextIndex = count - 1;
 170             }
 171         }
 172         final JInternalFrame f = fChildFrames.elementAt(nextIndex);
 173         activateFrame(f);
 174         fCurrentFrame = f;
 175     }
 176 
 177     /**
 178      * Activate the next child JInternalFrame, as determined by
 179      * the frames' Z-order.  If there is only one child frame, it
 180      * remains activated.  If there are no child frames, nothing
 181      * happens.
 182      */
 183     public void activateNextFrame() {
 184         switchFrame(true);
 185     }
 186 
 187     /** same as above but will activate a frame if none
 188      *  have been selected
 189      */
 190     public void activateNextFrame(final JInternalFrame f) {
 191         fInitialFrame = f;
 192         switchFrame(true);
 193     }
 194 
 195     /**
 196      * Activate the previous child JInternalFrame, as determined by
 197      * the frames' Z-order.  If there is only one child frame, it
 198      * remains activated.  If there are no child frames, nothing
 199      * happens.
 200      */
 201     public void activatePreviousFrame() {
 202         switchFrame(false);
 203     }
 204 }