1   /*
2    *
3    *  Copyright (c) 1998-2005, The University of Sheffield.
4    *
5    *  This file is part of GATE (see http://gate.ac.uk/), and is free
6    *  software, licenced under the GNU Library General Public License,
7    *  Version 2, June 1991 (in the distribution as file licence.html,
8    *  and also available at http://gate.ac.uk/gate/licence.html).
9    *
10   *  Valentin Tablan 20 Feb 2003
11   *
12   *  $Id: MenuLayout.java,v 1.8 2005/01/11 13:51:37 ian Exp $
13   */
14  
15  package gate.swing;
16  
17  import java.awt.*;
18  
19  import javax.swing.JPopupMenu;
20  import javax.swing.SwingUtilities;
21  
22  
23  /**
24   * A layout designed to allow Java menus to make better use of the screen
25   * real-estate. It will lay out the menu components in columns going from top
26   * to bottom and from left to right.
27   */
28  public class MenuLayout implements LayoutManager {
29  
30    /**
31     * Adds the specified component to the layout. Not used by this class.
32     * @param name the name of the component
33     * @param comp the the component to be added
34     */
35    public void addLayoutComponent(String name, Component comp) {}
36  
37    /**
38     * Removes the specified component from the layout. Not used by this class.
39     * @param comp the component to remove
40     */
41    public void removeLayoutComponent(Component comp) {}
42  
43    /**
44     * Returns the preferred dimensions for this layout given the components
45     * in the specified target container.
46     * @param target the component which needs to be laid out
47     * @see Container
48     * @see #minimumLayoutSize
49     */
50    public Dimension preferredLayoutSize(Container target) {
51      int membersCnt = target.getComponentCount();
52      Dimension[] componentPrefSizes = new Dimension[membersCnt];
53      //store the sizes
54      for(int i = 0; i < membersCnt; i++){
55        componentPrefSizes[i] = target.getComponent(i).getPreferredSize();
56      }
57      Dimension dim = getCompositeSize(target, componentPrefSizes);
58      return dim;
59    }
60  
61    /**
62     * Calculates the size of the target container given the sizes of the
63     * components.
64     * If the doLayout is <b>true</b> it will also ly out the container.
65     * Used by {@link #minimumLayoutSize} and {@link #preferredLayoutSize}.
66     * @param target
67     * @param componentSizes
68     * @return a {@link Dimension} value.
69     */
70    protected Dimension getCompositeSize(Container target,
71                                         Dimension[] componentSizes){
72      //find the origin of the popup
73      Point location = new Point(0, 0);
74      if(target.isShowing()){
75        location = target.getLocationOnScreen();
76      }else{
77        if(target instanceof JPopupMenu){
78          Component invoker = ((JPopupMenu)target).getInvoker();
79          if(invoker != null) location = invoker.getLocationOnScreen();
80        }
81      }
82  
83      //correct offscreen showing
84      if(location.x < 0 || location.y < 0){
85        location.x = Math.max(0, location.x);
86        location.y = Math.max(0, location.y);
87      }
88      //find the maximum size
89      Toolkit toolkit = Toolkit.getDefaultToolkit();
90      Rectangle contentsBounds = new Rectangle(toolkit.getScreenSize());
91      Insets screenInsets = new Insets(0, 0, 0, 0);
92      GraphicsConfiguration gc = findGraphicsConfiguration(target);
93      if (gc != null) {
94        contentsBounds = gc.getBounds();
95        screenInsets = toolkit.getScreenInsets(gc);
96      }else{
97      }
98  
99      // take screen insets (e.g. taskbar) into account
100     contentsBounds.width -= screenInsets.left + screenInsets.right;
101     contentsBounds.height -= screenInsets.top + screenInsets.bottom;
102     //take the location into account assuming that the largest side will be used
103     contentsBounds.height = Math.max(location.y,
104                                      contentsBounds.height - location.y);
105 
106     // take component insets into account
107     Insets insets = target.getInsets();
108     contentsBounds.width -= insets.left + insets.right;
109     contentsBounds.height -= insets.top + insets.bottom;
110     Dimension dim = new Dimension(0, 0);
111     int previousColumnsWidth = 0;
112     int previousColumnsHeight = 0;
113     for (int i = 0; i < componentSizes.length; i++) {
114       if ( (dim.height +
115             componentSizes[i].height) <= contentsBounds.height) {
116         //we can fit the current component in the current row
117         dim.height += componentSizes[i].height;
118         dim.width = Math.max(dim.width, componentSizes[i].width);
119       }
120       else {
121         //we need to start a new column
122         previousColumnsWidth += dim.width;
123         previousColumnsHeight = Math.max(previousColumnsHeight, dim.height);
124         dim.height = componentSizes[i].height;
125         dim.width = componentSizes[i].width;
126       }
127     }
128 
129     //Now dim contains the sizes for the last column
130     dim.height = Math.max(previousColumnsHeight, dim.height);
131     dim.width += previousColumnsWidth;
132     //add the target insets
133     dim.width += insets.left + insets.right;
134     dim.height += insets.top + insets.bottom;
135     return dim;
136   }
137 
138   /**
139    * Find the graphics configuration for the target popup (useful in case of
140    * multiple screens).
141    * @param target the component for which the configuration needs to be found.
142    * @return a GraphicsConfiguration value.
143    */
144   protected GraphicsConfiguration findGraphicsConfiguration(Component target){
145     GraphicsConfiguration gc = null;
146     if(!target.isShowing()){
147       if(target instanceof JPopupMenu){
148         Component invoker = ((JPopupMenu)target).getInvoker();
149         if(invoker != null) target = invoker;
150       }
151     }
152     if(target.isShowing()){
153       Point position = target.getLocationOnScreen();
154       Toolkit toolkit = Toolkit.getDefaultToolkit();
155       gc = target.getGraphicsConfiguration();
156       GraphicsEnvironment ge =
157           GraphicsEnvironment.getLocalGraphicsEnvironment();
158       GraphicsDevice[] gd = ge.getScreenDevices();
159       for (int i = 0; i < gd.length; i++) {
160         if (gd[i].getType() == GraphicsDevice.TYPE_RASTER_SCREEN) {
161           GraphicsConfiguration dgc =
162               gd[i].getDefaultConfiguration();
163           if (dgc.getBounds().contains(position)) {
164             gc = dgc;
165             break;
166           }
167         }
168       }
169     }
170     return gc;
171   }
172 
173   /**
174    * Returns the minimum dimensions needed to layout the components
175    * contained in the specified target container.
176    * @param target the component which needs to be laid out
177    * @see #preferredLayoutSize
178    */
179   public Dimension minimumLayoutSize(Container target) {
180     int membersCnt = target.getComponentCount();
181     Dimension[] componentMinSizes = new Dimension[membersCnt];
182     //store the sizes
183     for(int i = 0; i < membersCnt; i++){
184       componentMinSizes[i] = target.getComponent(i).getMinimumSize();
185     }
186     return getCompositeSize(target, componentMinSizes);
187   }
188 
189 
190   public void layoutContainer(Container target) {
191     Insets insets = target.getInsets();
192     Rectangle bounds = target.getBounds();
193     //correct off-screen showing
194     if(target.isShowing()){
195       Point locationOnScreen = target.getLocationOnScreen();
196       if(locationOnScreen.x < 0 || locationOnScreen.y < 0){
197         Window parent = SwingUtilities.getWindowAncestor(target);
198         int newx = Math.max(0, locationOnScreen.x);
199         int newy = Math.max(0, locationOnScreen.y);
200         parent.setLocation(newx, newy);
201       }
202     }
203     int maxheight = bounds.height - insets.bottom;
204     int compCnt = target.getComponentCount();
205     int y = insets.top;
206     int x = insets.left;
207     int rowWidth = 0;
208 
209     for (int i = 0; i < compCnt; i++) {
210       Component comp = target.getComponent(i);
211       if (comp.isVisible()) {
212         Dimension d = comp.getPreferredSize();
213         comp.setSize(d);
214         if (y + d.height <= maxheight) {
215           comp.setLocation(x, y);
216           y += d.height;
217           rowWidth = Math.max(rowWidth, d.width);
218         }
219         else {
220           //we need to start a new column
221           x += rowWidth;
222           rowWidth = 0;
223           y = insets.top;
224           comp.setLocation(x, y);
225           y += d.height;
226           rowWidth = Math.max(rowWidth, d.width);
227         }
228       }
229     }
230   }
231 }