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