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