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 a {@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(0, 0);
076 if(target.isShowing()){
077 location = target.getLocationOnScreen();
078 }else{
079 if(target instanceof JPopupMenu){
080 Component invoker = ((JPopupMenu)target).getInvoker();
081 if(invoker != null) location = 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(0, 0, 0, 0);
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(0, 0);
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 != null) target = 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 }
|