MenuLayout.java
001 /*
002  *
003  *  Copyright (c) 1995-2010, The University of Sheffield. See the file
004  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
005  *
006  *  This file is part of GATE (see http://gate.ac.uk/), and is free
007  *  software, licenced under the GNU Library General Public License,
008  *  Version 2, June 1991 (in the distribution as file licence.html,
009  *  and also available at http://gate.ac.uk/gate/licence.html).
010  *
011  *  Valentin Tablan 20 Feb 2003
012  *
013  *  $Id: MenuLayout.java 12656 2010-05-18 12:51:06Z ian_roberts $
014  */
015 
016 package gate.swing;
017 
018 import java.awt.*;
019 import java.util.Arrays;
020 import java.util.ArrayList;
021 
022 import javax.swing.JPopupMenu;
023 import javax.swing.SwingUtilities;
024 
025 
026 /**
027  * A layout designed to allow Java menus to make better use of the screen
028  * real-estate. It will lay out the menu components in columns going from top
029  * to bottom and from left to right.
030  */
031 public class MenuLayout implements LayoutManager {
032 
033   /**
034    * Adds the specified component to the layout. Not used by this class.
035    @param name the name of the component
036    @param comp the the component to be added
037    */
038   public void addLayoutComponent(String name, Component comp) {}
039 
040   /**
041    * Removes the specified component from the layout. Not used by this class.
042    @param comp the component to remove
043    */
044   public void removeLayoutComponent(Component comp) {}
045 
046   /**
047    * Returns the preferred dimensions for this layout given the components
048    * in the specified target container.
049    @param target the component which needs to be laid out
050    @see Container
051    @see #minimumLayoutSize
052    */
053   public Dimension preferredLayoutSize(Container target) {
054     int membersCnt = target.getComponentCount();
055     Dimension[] componentPrefSizes = new Dimension[membersCnt];
056     //store the sizes
057     for(int i = 0; i < membersCnt; i++){
058       componentPrefSizes[i= target.getComponent(i).getPreferredSize();
059     }
060     return getCompositeSize(target, componentPrefSizes);
061   }
062 
063   /**
064    * Calculates the size of the target container given the sizes of the
065    * components.
066    * If the doLayout is <b>true</b> it will also lay out the container.
067    * Used by {@link #minimumLayoutSize} and {@link #preferredLayoutSize}.
068    @param target  the component which needs to be laid out
069    @param componentSizes array of dimensions for each menu component
070    @return {@link Dimension} value.
071    */
072   protected Dimension getCompositeSize(Container target,
073                                        Dimension[] componentSizes){
074     //find the origin of the popup
075     Point location = new Point(00);
076     if(target.isShowing()){
077       location = target.getLocationOnScreen();
078     }else{
079       if(target instanceof JPopupMenu){
080         Component invoker = ((JPopupMenu)target).getInvoker();
081         if(invoker != nulllocation = invoker.getLocationOnScreen();
082       }
083     }
084 
085     //find the maximum size
086     Toolkit toolkit = Toolkit.getDefaultToolkit();
087     Rectangle contentsBounds = new Rectangle(toolkit.getScreenSize());
088     Insets screenInsets = new Insets(0000);
089     GraphicsConfiguration gc = findGraphicsConfiguration(target);
090     if (gc != null) {
091       contentsBounds = gc.getBounds();
092       screenInsets = toolkit.getScreenInsets(gc);
093     }
094 
095     // take screen insets (e.g. taskbar) into account
096     contentsBounds.width -= screenInsets.left + screenInsets.right;
097     contentsBounds.height -= screenInsets.top + screenInsets.bottom;
098     //take the location into account assuming that the largest side will be used
099     contentsBounds.height = Math.max(location.y,
100                                      contentsBounds.height - location.y);
101 
102     // take component insets into account
103     Insets insets = target.getInsets();
104     contentsBounds.width -= insets.left + insets.right;
105     contentsBounds.height -= insets.top + insets.bottom;
106     Dimension dim = new Dimension(00);
107     int previousColumnsWidth = 0;
108     int previousColumnsHeight = 0;
109     int columnIndex = 1;
110     int firstComponentIndexForColumn = 0;
111     columnForComponentIndex = new int[componentSizes.length];
112     preferredWidthForColumn = new ArrayList<Integer>();
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         Arrays.fill(columnForComponentIndex, firstComponentIndexForColumn,
123           i, columnIndex);
124         preferredWidthForColumn.add(dim.width);
125         firstComponentIndexForColumn = i;
126         columnIndex++;
127         previousColumnsWidth += dim.width;
128         previousColumnsHeight = Math.max(previousColumnsHeight, dim.height);
129         dim.height = componentSizes[i].height;
130         dim.width = componentSizes[i].width;
131       }
132     }
133 
134     Arrays.fill(columnForComponentIndex, firstComponentIndexForColumn,
135       columnForComponentIndex.length, columnIndex);
136     preferredWidthForColumn.add(dim.width);
137     //Now dim contains the sizes for the last column
138     dim.height = Math.max(previousColumnsHeight, dim.height);
139     dim.width += previousColumnsWidth;
140     //add the target insets
141     dim.width += insets.left + insets.right;
142     dim.height += insets.top + insets.bottom;
143     return dim;
144   }
145 
146   /**
147    * Find the graphics configuration for the target popup (useful in case of
148    * multiple screens).
149    @param target the component for which the configuration needs to be found.
150    @return a GraphicsConfiguration value.
151    */
152   protected GraphicsConfiguration findGraphicsConfiguration(Component target){
153     GraphicsConfiguration gc = null;
154     if(!target.isShowing()){
155       if(target instanceof JPopupMenu){
156         Component invoker = ((JPopupMenu)target).getInvoker();
157         if(invoker != nulltarget = invoker;
158       }
159     }
160     if(target.isShowing()){
161       Point position = target.getLocationOnScreen();
162       gc = target.getGraphicsConfiguration();
163       GraphicsEnvironment ge =
164         GraphicsEnvironment.getLocalGraphicsEnvironment();
165       for (GraphicsDevice gd : ge.getScreenDevices()) {
166         if (gd.getType() == GraphicsDevice.TYPE_RASTER_SCREEN) {
167           GraphicsConfiguration dgc = gd.getDefaultConfiguration();
168           if (dgc.getBounds().contains(position)) {
169             gc = dgc;
170             break;
171           }
172         }
173       }
174     }
175     return gc;
176   }
177 
178   /**
179    * Returns the minimum dimensions needed to layout the components
180    * contained in the specified target container.
181    @param target the component which needs to be laid out
182    @see #preferredLayoutSize
183    */
184   public Dimension minimumLayoutSize(Container target) {
185     int membersCnt = target.getComponentCount();
186     Dimension[] componentMinSizes = new Dimension[membersCnt];
187     //store the sizes
188     for(int i = 0; i < membersCnt; i++){
189       componentMinSizes[i= target.getComponent(i).getMinimumSize();
190     }
191     return getCompositeSize(target, componentMinSizes);
192   }
193 
194 
195   public void layoutContainer(Container target) {
196     Insets insets = target.getInsets();
197     Rectangle bounds = target.getBounds();
198     int maxheight = bounds.height - insets.bottom;
199     int compCnt = target.getComponentCount();
200     int y = insets.top;
201     int x = insets.left;
202     int rowWidth = 0;
203 
204     for (int i = 0; i < compCnt; i++) {
205       Component comp = target.getComponent(i);
206       if (comp.isVisible()) {
207         Dimension d = comp.getPreferredSize();
208         comp.setSize(d);
209         if (y + d.height <= maxheight) {
210           comp.setLocation(x, y);
211           y += d.height;
212           rowWidth = Math.max(rowWidth, d.width);
213         }
214         else {
215           //we need to start a new column
216           x += rowWidth;
217           rowWidth = 0;
218           y = insets.top;
219           comp.setLocation(x, y);
220           y += d.height;
221           rowWidth = Math.max(rowWidth, d.width);
222         }
223       }
224     }
225   }
226 
227   public int getColumnForComponentIndex(int index) {
228     return columnForComponentIndex[index];
229   }
230 
231   public int getPreferredWidthForColumn(int index) {
232     return preferredWidthForColumn.get(index-1);
233   }
234 
235   private int[] columnForComponentIndex;
236   private ArrayList<Integer> preferredWidthForColumn;
237 }