Merge branch 'develop' into feature/JAL-3416_update_to_flatlaf_3.1.1_with_unpacked_na...
[jalview.git] / src / jalview / jbgui / GSplitFrame.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.jbgui;
22
23 import java.awt.Component;
24 import java.awt.MouseInfo;
25 import java.awt.Point;
26 import java.awt.Rectangle;
27
28 import javax.swing.JInternalFrame;
29 import javax.swing.JSplitPane;
30 import javax.swing.plaf.basic.BasicInternalFrameUI;
31
32 import jalview.util.Platform;
33
34 public class GSplitFrame extends JInternalFrame
35 {
36   protected static final int DIVIDER_SIZE = 5;
37
38   private static final long serialVersionUID = 1L;
39
40   private GAlignFrame topFrame;
41
42   private GAlignFrame bottomFrame;
43
44   private JSplitPane splitPane;
45
46   /*
47    * proportional position of split divider; saving this allows it to be
48    * restored after hiding one half and resizing
49    */
50   private double dividerRatio;
51
52   /**
53    * Constructor
54    * 
55    * @param top
56    * @param bottom
57    */
58   public GSplitFrame(GAlignFrame top, GAlignFrame bottom)
59   {
60     setFrameIcon(null);
61     setName("jalview-splitframe");
62     this.topFrame = top;
63     this.bottomFrame = bottom;
64
65     hideTitleBars();
66
67     addSplitPane();
68   }
69
70   /**
71    * Create and add the split pane containing the top and bottom components.
72    */
73   protected void addSplitPane()
74   {
75     splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, topFrame,
76             bottomFrame);
77     splitPane.setVisible(true);
78
79     /*
80      * set divider split at 50:50, or restore saved split if loading from
81      * project
82      */
83     int topFrameHeight = topFrame.getHeight();
84     splitPane.setDividerSize(DIVIDER_SIZE);
85     if (topFrameHeight == 0)
86     {
87       setRelativeDividerLocation(0.5d); // as a proportion
88     }
89     else
90     {
91       int dividerPosition = topFrameHeight + DIVIDER_SIZE / 2;
92       splitPane.setDividerLocation(dividerPosition); // absolute position
93     }
94     splitPane.setResizeWeight(0.5d);
95     add(splitPane);
96   }
97
98   /**
99    * Try to hide the title bars as a waste of precious space.
100    * 
101    * @see http
102    *      ://stackoverflow.com/questions/7218971/java-method-works-on-windows
103    *      -but-not-macintosh -java
104    */
105   protected void hideTitleBars()
106   {
107     if (Platform.isAMacAndNotJS())
108     {
109       // this saves some space - but doesn't hide the title bar
110       topFrame.putClientProperty("JInternalFrame.isPalette", true);
111       // topFrame.getRootPane().putClientProperty("Window.style", "small");
112       bottomFrame.putClientProperty("JInternalFrame.isPalette", true);
113     }
114     else
115     {
116       ((BasicInternalFrameUI) topFrame.getUI()).setNorthPane(null);
117       ((BasicInternalFrameUI) bottomFrame.getUI()).setNorthPane(null);
118     }
119   }
120
121   public GAlignFrame getTopFrame()
122   {
123     return topFrame;
124   }
125
126   public GAlignFrame getBottomFrame()
127   {
128     return bottomFrame;
129   }
130
131   /**
132    * Returns the split pane component the mouse is in, or null if neither.
133    * 
134    * @return
135    */
136   protected GAlignFrame getFrameAtMouse()
137   {
138     Point loc = MouseInfo.getPointerInfo().getLocation();
139
140     if (isIn(loc, splitPane.getTopComponent()))
141     {
142       return getTopFrame();
143     }
144     else if (isIn(loc, splitPane.getBottomComponent()))
145     {
146       return getBottomFrame();
147     }
148     return null;
149   }
150
151   private boolean isIn(Point loc, Component comp)
152   {
153     if (!comp.isVisible())
154     {
155       return false;
156     }
157     Point p = comp.getLocationOnScreen();
158     Rectangle r = new Rectangle(p.x, p.y, comp.getWidth(),
159             comp.getHeight());
160     return r.contains(loc);
161   }
162
163   /**
164    * Makes the complement of the specified split component visible or hidden,
165    * restoring or saving the position of the split divide.
166    */
167   public void setComplementVisible(Object alignFrame, boolean show)
168   {
169     /*
170      * save divider ratio on hide, restore on show
171      */
172     if (show)
173     {
174       setRelativeDividerLocation(dividerRatio);
175     }
176     else
177     {
178       this.dividerRatio = splitPane.getDividerLocation()
179               / (double) (splitPane.getHeight()
180                       - splitPane.getDividerSize());
181     }
182
183     if (alignFrame == this.topFrame)
184     {
185       this.bottomFrame.setVisible(show);
186     }
187     else if (alignFrame == this.bottomFrame)
188     {
189       this.topFrame.setVisible(show);
190     }
191
192     validate();
193   }
194
195   /**
196    * Set the divider location as a proportion (0 <= r <= 1) of the height <br>
197    * Warning: this overloads setDividerLocation(int), and getDividerLocation()
198    * returns the int (pixel count) value
199    * 
200    * @param r
201    */
202   public void setRelativeDividerLocation(double r)
203   {
204     this.dividerRatio = r;
205     splitPane.setDividerLocation(r);
206   }
207
208   /**
209    * Sets the divider location (in pixels from top)
210    * 
211    * @return
212    */
213   protected void setDividerLocation(int p)
214   {
215     splitPane.setDividerLocation(p);
216   }
217
218   /**
219    * Returns the divider location (in pixels from top)
220    * 
221    * @return
222    */
223   protected int getDividerLocation()
224   {
225     return splitPane.getDividerLocation();
226   }
227 }