MenuLayout.java
001 /*
002  *
003  *  Copyright (c) 1995-2012, 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 17530 2014-03-04 15:57:43Z markagreenwood $
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 
024 
025 /**
026  * A layout designed to allow Java menus to make better use of the screen
027  * real-estate. It will lay out the menu components in columns going from top
028  * to bottom and from left to right.
029  */
030 public class MenuLayout implements LayoutManager {
031 
032   /**
033    * Adds the specified component to the layout. Not used by this class.
034    @param name the name of the component
035    @param comp the the component to be added
036    */
037   @Override
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   @Override
045   public void removeLayoutComponent(Component comp) {}
046 
047   /**
048    * Returns the preferred dimensions for this layout given the components
049    * in the specified target container.
050    @param target the component which needs to be laid out
051    @see Container
052    @see #minimumLayoutSize
053    */
054   @Override
055   public Dimension preferredLayoutSize(Container target) {
056     int membersCnt = target.getComponentCount();
057     Dimension[] componentPrefSizes = new Dimension[membersCnt];
058     //store the sizes
059     for(int i = 0; i < membersCnt; i++){
060       componentPrefSizes[i= target.getComponent(i).getPreferredSize();
061     }
062     return getCompositeSize(target, componentPrefSizes);
063   }
064 
065   /**
066    * Calculates the size of the target container given the sizes of the
067    * components.
068    * If the doLayout is <b>true</b> it will also lay out the container.
069    * Used by {@link #minimumLayoutSize} and {@link #preferredLayoutSize}.
070    @param target  the component which needs to be laid out
071    @param componentSizes array of dimensions for each menu component
072    @return {@link Dimension} value.
073    */
074   protected Dimension getCompositeSize(Container target,
075                                        Dimension[] componentSizes){
076     //find the origin of the popup
077     Point location = new Point(00);
078     if(target.isShowing()){
079       location = target.getLocationOnScreen();
080     }else{
081       if(target instanceof JPopupMenu){
082         Component invoker = ((JPopupMenu)target).getInvoker();
083         if(invoker != nulllocation = invoker.getLocationOnScreen();
084       }
085     }
086 
087     //find the maximum size
088     Toolkit toolkit = Toolkit.getDefaultToolkit();
089     Rectangle contentsBounds = new Rectangle(toolkit.getScreenSize());
090     Insets screenInsets = new Insets(0000);
091     GraphicsConfiguration gc = findGraphicsConfiguration(target);
092     if (gc != null) {
093       contentsBounds = gc.getBounds();
094       screenInsets = toolkit.getScreenInsets(gc);
095     }
096 
097     // take screen insets (e.g. taskbar) into account
098     contentsBounds.width -= screenInsets.left + screenInsets.right;
099     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(00);
109     int previousColumnsWidth = 0;
110     int previousColumnsHeight = 0;
111     int columnIndex = 1;
112     int firstComponentIndexForColumn = 0;
113     columnForComponentIndex = new int[componentSizes.length];
114     preferredWidthForColumn = new ArrayList<Integer>();
115     for (int i = 0; i < componentSizes.length; i++) {
116       if ( (dim.height +
117             componentSizes[i].height<= contentsBounds.height) {
118         //we can fit the current component in the current row
119         dim.height += componentSizes[i].height;
120         dim.width = Math.max(dim.width, componentSizes[i].width);
121       }
122       else {
123         //we need to start a new column
124         Arrays.fill(columnForComponentIndex, firstComponentIndexForColumn,
125           i, columnIndex);
126         preferredWidthForColumn.add(dim.width);
127         firstComponentIndexForColumn = i;
128         columnIndex++;
129         previousColumnsWidth += dim.width;
130         previousColumnsHeight = Math.max(previousColumnsHeight, dim.height);
131         dim.height = componentSizes[i].height;
132         dim.width = componentSizes[i].width;
133       }
134     }
135 
136     Arrays.fill(columnForComponentIndex, firstComponentIndexForColumn,
137       columnForComponentIndex.length, columnIndex);
138     preferredWidthForColumn.add(dim.width);
139     //Now dim contains the sizes for the last column
140     dim.height = Math.max(previousColumnsHeight, dim.height);
141     dim.width += previousColumnsWidth;
142     //add the target insets
143     dim.width += insets.left + insets.right;
144     dim.height += insets.top + insets.bottom;
145     return dim;
146   }
147 
148   /**
149    * Find the graphics configuration for the target popup (useful in case of
150    * multiple screens).
151    @param target the component for which the configuration needs to be found.
152    @return a GraphicsConfiguration value.
153    */
154   protected GraphicsConfiguration findGraphicsConfiguration(Component target){
155     GraphicsConfiguration gc = null;
156     if(!target.isShowing()){
157       if(target instanceof JPopupMenu){
158         Component invoker = ((JPopupMenu)target).getInvoker();
159         if(invoker != nulltarget = invoker;
160       }
161     }
162     if(target.isShowing()){
163       Point position = target.getLocationOnScreen();
164       gc = target.getGraphicsConfiguration();
165       GraphicsEnvironment ge =
166         GraphicsEnvironment.getLocalGraphicsEnvironment();
167       for (GraphicsDevice gd : ge.getScreenDevices()) {
168         if (gd.getType() == GraphicsDevice.TYPE_RASTER_SCREEN) {
169           GraphicsConfiguration dgc = gd.getDefaultConfiguration();
170           if (dgc.getBounds().contains(position)) {
171             gc = dgc;
172             break;
173           }
174         }
175       }
176     }
177     return gc;
178   }
179 
180   /**
181    * Returns the minimum dimensions needed to layout the components
182    * contained in the specified target container.
183    @param target the component which needs to be laid out
184    @see #preferredLayoutSize
185    */
186   @Override
187   public Dimension minimumLayoutSize(Container target) {
188     int membersCnt = target.getComponentCount();
189     Dimension[] componentMinSizes = new Dimension[membersCnt];
190     //store the sizes
191     for(int i = 0; i < membersCnt; i++){
192       componentMinSizes[i= target.getComponent(i).getMinimumSize();
193     }
194     return getCompositeSize(target, componentMinSizes);
195   }
196 
197 
198   @Override
199   public void layoutContainer(Container target) {
200     Insets insets = target.getInsets();
201     Rectangle bounds = target.getBounds();
202     int maxheight = bounds.height - insets.bottom;
203     int compCnt = target.getComponentCount();
204     int y = insets.top;
205     int x = insets.left;
206     int rowWidth = 0;
207 
208     for (int i = 0; i < compCnt; i++) {
209       Component comp = target.getComponent(i);
210       if (comp.isVisible()) {
211         Dimension d = comp.getPreferredSize();
212         comp.setSize(d);
213         if (y + d.height <= maxheight) {
214           comp.setLocation(x, y);
215           y += d.height;
216           rowWidth = Math.max(rowWidth, d.width);
217         }
218         else {
219           //we need to start a new column
220           x += rowWidth;
221           rowWidth = 0;
222           y = insets.top;
223           comp.setLocation(x, y);
224           y += d.height;
225           rowWidth = Math.max(rowWidth, d.width);
226         }
227       }
228     }
229   }
230 
231   public int getColumnForComponentIndex(int index) {
232     return columnForComponentIndex[index];
233   }
234 
235   public int getPreferredWidthForColumn(int index) {
236     return preferredWidthForColumn.get(index-1);
237   }
238 
239   private int[] columnForComponentIndex;
240   private ArrayList<Integer> preferredWidthForColumn;
241 }