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