1 /* 2 * @(#)Quaqua16LayoutStyle.java 1.2 2007-12-18 3 * 4 * Copyright (c) 2007 Werner Randelshofer 5 * Staldenmattweg 2, Immensee, CH-6405, Switzerland. 6 * All rights reserved. 7 * 8 * The copyright of this software is owned by Werner Randelshofer. 9 * You may not use, copy or modify this software, except in 10 * accordance with the license agreement you entered into with 11 * Werner Randelshofer. For details see accompanying license terms. 12 */ 13 14 package ch.randelshofer.quaqua; 15 16 import java.awt.*; 17 import javax.swing.*; 18 import java.lang.reflect.*; 19 import java.util.*; 20 /** 21 * A Quaqua16LayoutStyle can be queried for the preferred gaps between two 22 * JComponents, or between a JComponent and a parent Container. 23 * <p> 24 * To compile this class, you need Java 1.6. 25 * 26 * @author Werner Randelshofer 27 * @version 1.2 2007-12-18 Bumped minimal treshold for indentation up from 3 28 * to 8. 29 * <br>1.0 2007-07-23 Created. 30 */ 31 public class Quaqua16LayoutStyle extends LayoutStyle { 32 private final static boolean DEBUG = false; 33 34 /** Mini size style. */ 35 private final static int MINI = 0; 36 /** Small size style. */ 37 private final static int SMALL = 1; 38 /** Regular size style. */ 39 private final static int REGULAR = 2; 40 41 /** 42 * The containerGapDefinitions array defines the preferred insets (child gaps) 43 * of a parent container towards one of its child components. 44 * 45 * Note: As of now, we do not yet specify the preferred gap from a child 46 * to its parent. Therefore we may not be able to treat all special cases. 47 * 48 * This array is used to initialize the containerGaps HashMap. 49 * 50 * The array has the following structure, which is supposed to be a 51 * a compromise between legibility and code size. 52 * containerGapDefinitions[0..n] = preferred insets for some parent UI's 53 * containerGapDefinitions[][0..m-3] = name of parent UI, 54 * optionally followed by a full stop and 55 * a style name 56 * containerGapDefinitions[][m-2] = mini insets 57 * containerGapDefinitions[][m-1] = small insets 58 * containerGapDefinitions[][m] = regular insets 59 */ 60 private final static Object[][] containerGapDefinitions = { 61 // Format: 62 // { list of parent UI's, 63 // mini insets, small insets, regular insets } 64 65 { "TabbedPaneUI", 66 new Insets(6,10,10,10), new Insets(6,10,10,12), new Insets(12,20,20,20) 67 }, 68 69 // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGLayout/chapter_19_section_3.html#//apple_ref/doc/uid/TP30000360/DontLinkElementID_27 70 // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGLayout/chapter_19_section_3.html#//apple_ref/doc/uid/TP30000360/DontLinkElementID_26 71 // note for small and mini size: leave 8 to 10 pixels on top 72 // note for regular size: leave only 12 pixel at top if tabbed pane UI 73 { "RootPaneUI", 74 new Insets(8,10,10,10), new Insets(8,10,10,12), new Insets(14,20,20,20) 75 }, 76 77 // These child gaps are used for all other components 78 { "default", 79 new Insets(8,10,10,10), new Insets(8,10,10,12), new Insets(14,20,20,20) 80 }, 81 }; 82 83 /** 84 * The relatedGapDefinitions table defines the preferred gaps 85 * of one party of two related components. 86 * 87 * The effective preferred gap is the maximum of the preferred gaps of 88 * both parties. 89 * 90 * This array is used to initialize the relatedGaps HashMap. 91 * 92 * The array has the following structure, which is supposed to be a 93 * a compromise between legibility and code size. 94 * containerGapDefinitions[0..n] = preferred gaps for a party of a two related UI's 95 * containerGapDefinitions[][0..m-3] = name of UI 96 * optionally followed by a full stop and 97 * a style name 98 * containerGapDefinitions[][m-2] = mini insets 99 * containerGapDefinitions[][m-1] = small insets 100 * containerGapDefinitions[][m] = regular insets 101 */ 102 private final static Object[][] relatedGapDefinitions = { 103 // Format: 104 // { list of UI's, 105 // mini insets, small insets, regular insets } 106 107 // Push Button: 108 // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF104 109 { "ButtonUI", "ButtonUI.push", "ButtonUI.text", 110 "ToggleButtonUI.push", "ToggleButtonUI.text", 111 new Insets(8,8,8,8), new Insets(10,10,10,10), new Insets(12,12,12,12) 112 }, 113 114 // Metal Button 115 // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF187 116 { "ButtonUI.metal", "ToggleButtonUI.metal", 117 new Insets(8,8,8,8), new Insets(8,8,8,8), new Insets(12,12,12,12) 118 }, 119 120 // Bevel Button (Rounded and Square) 121 // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF112 122 { "ButtonUI.bevel", "ButtonUI.toggle", "ButtonUI.square", 123 "ToggleButtonUI", "ToggleButtonUI.bevel", "ToggleButtonUI.square", "ToggleButtonUI.toggle", 124 new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0) 125 }, 126 127 // Bevel Button (Rounded and Square) 128 // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF112 129 { "ButtonUI.bevel.largeIcon", "ToggleButtonUI.bevel.largeIcon", 130 new Insets(8,8,8,8), new Insets(8,8,8,8), new Insets(8,8,8,8) 131 }, 132 133 // Icon Button 134 // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF189 135 { "ButtonUI.icon", 136 new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0) 137 }, 138 { "ButtonUI.icon.largeIcon", 139 new Insets(8,8,8,8), new Insets(8,8,8,8), new Insets(8,8,8,8) 140 }, 141 142 // Round Button 143 // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF191 144 { "ButtonUI.round", "ToggleButtonUI.round", 145 new Insets(12,12,12,12), new Insets(12,12,12,12), new Insets(12,12,12,12) 146 }, 147 148 // Help Button 149 // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF193 150 { "ButtonUI.help", 151 new Insets(12,12,12,12), new Insets(12,12,12,12), new Insets(12,12,12,12) 152 }, 153 154 // Segmented Control 155 // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_3.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF196 156 { "ButtonUI.toggleCenter", "ToggleButtonUI.toggleCenter", 157 new Insets(8,0,8,0), new Insets(10,0,10,0), new Insets(12,0,12,0) 158 }, 159 { "ButtonUI.toggleEast", "ToggleButtonUI.toggleEast", 160 new Insets(8,0,8,8), new Insets(10,0,10,10), new Insets(12,0,12,12) 161 }, 162 { "ButtonUI.toggleWest", "ToggleButtonUI.toggleWest", 163 new Insets(8,8,8,0), new Insets(10,10,10,0), new Insets(12,12,12,0) 164 }, 165 166 { "ButtonUI.toolBarTab", "ToggleButtonUI.toolBarTab", 167 new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0) 168 }, 169 170 // Color Well Button 171 // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_3.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF213 172 { "ButtonUI.colorWell", "ToggleButtonUI.colorWell", 173 new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0) 174 }, 175 176 // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_3.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF198 177 // FIXME - The following values are given in the AHIG. 178 // In reality, the values further below seem to be more appropriate. 179 // Which ones are right? 180 //{ "CheckBoxUI", new Insets(7, 5, 7, 5), new Insets(8, 6, 8, 6), new Insets(8, 8, 8, 8) }, 181 { "CheckBoxUI", 182 new Insets(6, 5, 6, 5), new Insets(7, 6, 7, 6), new Insets(7, 6, 7, 6) 183 }, 184 185 // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_3.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF198 186 { "ComboBoxUI", 187 new Insets(8, 5, 8, 5), new Insets(10, 6, 10, 6), new Insets(12, 8, 12, 8) 188 }, 189 190 191 // There is no spacing given for labels in Apples Guidelines. 192 // We use the values here, which is the minimum of the spacing of all 193 // other components. 194 { "LabelUI", 195 new Insets(6, 8, 6, 8), new Insets(6, 8, 6, 8), new Insets(6, 8, 6, 8) 196 }, 197 198 // ? spacing not given 199 { "ListUI", 200 new Insets(5, 5, 5, 5), new Insets(6, 6, 6, 6), new Insets(6, 6, 6, 6) 201 }, 202 203 // ? spacing not given 204 { "PanelUI", 205 new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0) 206 }, 207 208 // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_5.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF106 209 // ? spacing not given 210 { "ProgressBarUI", 211 new Insets(8,8,8,8), new Insets(10,10,10,10), new Insets(12,12,12,12) 212 }, 213 214 // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_3.html#//apple_ref/doc/uid/20000957-TP30000359-BIAHBFAD 215 { "RadioButtonUI", 216 new Insets(5, 5, 5, 5), new Insets(6, 6, 6, 6), new Insets(6, 6, 6, 6) 217 }, 218 219 //http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_6.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF114 220 // ? spacing not given. We use the same like for text fields 221 { "ScrollPaneUI", 222 new Insets(6, 8, 6, 8), new Insets(6, 8, 6, 8), new Insets(8, 10, 8, 10) 223 }, 224 225 //http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_8.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF214 226 // ? spacing not given 227 //http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGLayout/chapter_19_section_2.html#//apple_ref/doc/uid/20000957-TP30000360-CHDEACGD 228 { "SeparatorUI", 229 new Insets(8, 8, 8, 8), new Insets(10, 10, 10, 10), new Insets(12, 12, 12, 12) 230 }, 231 232 // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_4.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF115 233 { "SliderUI.horizontal", 234 new Insets(6,8,6,8), new Insets(6,10,6,10), new Insets(6,12,6,12) 235 }, 236 { "SliderUI.vertical", 237 new Insets(8,8,8,8), new Insets(10,10,10,10), new Insets(12,12,12,12) 238 }, 239 240 //http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_4.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF204 241 { "SpinnerUI", 242 new Insets(6, 8, 6, 8), new Insets(6, 8, 6, 8), new Insets(8, 10, 8, 10) 243 }, 244 245 // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_7.html#//apple_ref/doc/uid/20000957-TP30000359-CHDDBIJE 246 // ? spacing not given 247 { "SplitPaneUI", 248 new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0) 249 }, 250 // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_7.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF105 251 // ? spacing not given 252 { "TabbedPaneUI", 253 new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0) 254 }, 255 { "TableUI", 256 new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0) 257 }, 258 // ? spacing not given 259 { "TextAreaUI", "EditorPaneUI", "TextPaneUI", 260 new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0) 261 }, 262 //http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_6.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF225 263 { "TextFieldUI", "FormattedTextFieldUI", "PasswordFieldUI", 264 new Insets(6, 8, 6, 8), new Insets(6, 8, 6, 8), new Insets(8, 10, 8, 10) 265 }, 266 267 // ? spacing not given 268 { "TreeUI", 269 new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0) 270 }, 271 }; 272 private final static Object[][] unrelatedGapDefinitions = { 273 // UI, mini, small, regular 274 { "ButtonUI.help", 275 new Insets(24,24,24,24), new Insets(24,24,24,24), new Insets(24,24,24,24) 276 }, 277 { "default", 278 new Insets(12, 12, 12, 12), new Insets(14, 14, 14, 14), new Insets(16, 16, 16, 16) 279 }, 280 }; 281 /** 282 * The indentGapDefinitions table defines the preferred indentation 283 * for components that are indented after the specified component. 284 * 285 * This array is used to initialize the indentGaps HashMap. 286 * 287 * The array has the following structure, which is supposed to be a 288 * a compromise between legibility and code size. 289 * indentGapDefinitions[0..n] = preferred gaps for a party of a two related UI's 290 * indentGapDefinitions[][0..m-3] = name of UI 291 * optionally followed by a full stop and 292 * a style name 293 * indentGapDefinitions[][m-2] = mini insets 294 * indentGapDefinitions[][m-1] = small insets 295 * indentGapDefinitions[][m] = regular insets 296 */ 297 private final static Object[][] indentGapDefinitions = { 298 // UI, mini, small, regular 299 { "default", 300 new Insets(16, 16, 16, 16), new Insets(20, 20, 20, 20), new Insets(25, 25, 25, 25) 301 }, 302 }; 303 304 /** 305 * Creates a hash map for the specified definitions array. 306 * 307 * Each entry of the hash map has the name of an UI (optionally followed by 308 * a full stop and a style name) as its key <String> 309 * and an array of Insets as its value <Insets[]>. 310 */ createMap(Object[][] definitions)311 private static HashMap createMap(Object[][] definitions) { 312 HashMap map = new HashMap(); 313 for (int i=0; i < definitions.length; i++) { 314 int keys = 0; 315 while (keys < definitions[i].length 316 && (definitions[i][keys] instanceof String)) { 317 keys++; 318 } 319 Insets[] values = new Insets[definitions[i].length - keys]; 320 for (int j=keys; j < definitions[i].length; j++) { 321 values[j-keys] = (Insets) definitions[i][j]; 322 } 323 for (int j=0; j < keys; j++) { 324 map.put(definitions[i][j], values); 325 } 326 } 327 return map; 328 } 329 330 /** 331 * The relatedGapDefinitions table defines the preferred gaps 332 * of one party of two related components. 333 */ 334 private final static HashMap relatedGaps = createMap(relatedGapDefinitions); 335 /** 336 * The unrelatedGapDefinitions table defines the preferred gaps 337 * of one party of two unrelated components. 338 */ 339 private final static HashMap unrelatedGaps = createMap(unrelatedGapDefinitions); 340 /** 341 * The containerGapDefinitions array defines the preferred insets (child gaps) 342 * of a parent component towards one of its children. 343 */ 344 private final static HashMap containerGaps = createMap(containerGapDefinitions); 345 /** 346 * The indentGapDefinitions table defines the preferred indentation 347 * for components that are indented after the specified component. 348 */ 349 private final static HashMap indentGaps = createMap(indentGapDefinitions); 350 351 /** 352 * Creates a new instance. 353 */ Quaqua16LayoutStyle()354 public Quaqua16LayoutStyle() { 355 } 356 357 /** 358 * Returns the amount of space to use between two components. 359 * The return value indicates the distance to place 360 * <code>component2</code> relative to <code>component1</code>. 361 * For example, the following returns the amount of space to place 362 * between <code>component2</code> and <code>component1</code> 363 * when <code>component2</code> is placed vertically above 364 * <code>component1</code>: 365 * <pre> 366 * int gap = getPreferredGap(component1, component2, 367 * LayoutStyle.RELATED, 368 * SwingConstants.NORTH, parent); 369 * </pre> 370 * The <code>type</code> parameter indicates the type 371 * of gap being requested. It can be one of the following values: 372 * <table> 373 * <tr><td><code>RELATED</code> 374 * <td>If the two components will be contained in 375 * the same parent and are showing similar logically related 376 * items, use <code>RELATED</code>. 377 * <tr><td><code>UNRELATED</code> 378 * <td>If the two components will be 379 * contained in the same parent but show logically unrelated items 380 * use <code>UNRELATED</code>. 381 * <tr><td><code>INDENT</code> 382 * <td>Used to obtain the preferred distance to indent a component 383 * relative to another. For example, if you want to horizontally 384 * indent a JCheckBox relative to a JLabel use <code>INDENT</code>. 385 * This is only useful for the horizontal axis. 386 * </table> 387 * <p> 388 * It's important to note that some look and feels may not distinguish 389 * between <code>RELATED</code> and <code>UNRELATED</code>. 390 * <p> 391 * The return value is not intended to take into account the 392 * current size and position of <code>component2</code> or 393 * <code>component1</code>. The return value may take into 394 * consideration various properties of the components. For 395 * example, the space may vary based on font size, or the preferred 396 * size of the component. 397 * 398 * @param component1 the <code>JComponent</code> 399 * <code>component2</code> is being placed relative to 400 * @param component2 the <code>JComponent</code> being placed 401 * @param type how the two components are being placed 402 * @param position the position <code>component2</code> is being placed 403 * relative to <code>component1</code>; one of 404 * <code>SwingConstants.NORTH</code>, 405 * <code>SwingConstants.SOUTH</code>, 406 * <code>SwingConstants.EAST</code> or 407 * <code>SwingConstants.WEST</code> 408 * @param parent the parent of <code>component2</code>; this may differ 409 * from the actual parent and may be null 410 * @return the amount of space to place between the two components 411 * @throws IllegalArgumentException if <code>position</code> is not 412 * one of <code>SwingConstants.NORTH</code>, 413 * <code>SwingConstants.SOUTH</code>, 414 * <code>SwingConstants.EAST</code> or 415 * <code>SwingConstants.WEST</code>; <code>type</code> not one 416 * of <code>INDENT</code>, <code>RELATED</code> 417 * or <code>UNRELATED</code>; or <code>component1</code> or 418 * <code>component2</code> is null 419 */ getPreferredGap(JComponent component1, JComponent component2, javax.swing.LayoutStyle.ComponentPlacement type, int position, Container parent)420 public int getPreferredGap(JComponent component1, JComponent component2, 421 javax.swing.LayoutStyle.ComponentPlacement type, int position, Container parent) { 422 int result; 423 424 if (type == javax.swing.LayoutStyle.ComponentPlacement.INDENT) { 425 // Compute gap 426 int sizeStyle = getSizeStyle(component1); 427 Insets vgap = getVisualIndent(component1); 428 Insets pgap = getPreferredGap(component1, javax.swing.LayoutStyle.ComponentPlacement.INDENT, sizeStyle); 429 switch (position) { 430 case SwingConstants.NORTH : 431 result = (vgap.bottom > 8) ? vgap.bottom : pgap.bottom; 432 break; 433 case SwingConstants.SOUTH : 434 result = (vgap.top > 8) ? vgap.top : pgap.top; 435 break; 436 case SwingConstants.EAST : 437 result = (vgap.left > 8) ? vgap.left : pgap.left; 438 break; 439 case SwingConstants.WEST : 440 default : 441 result = (vgap.right > 8) ? vgap.right : pgap.right; 442 break; 443 } 444 445 // Compensate for visual margin 446 Insets visualMargin2 = getVisualMargin(component2); 447 switch (position) { 448 case SwingConstants.NORTH : 449 result -= visualMargin2.bottom; 450 break; 451 case SwingConstants.SOUTH : 452 result -= visualMargin2.top; 453 break; 454 case SwingConstants.EAST : 455 result -= visualMargin2.left; 456 break; 457 case SwingConstants.WEST : 458 result -= visualMargin2.right; 459 default : 460 break; 461 } 462 } else { 463 464 // If the two components have different size styles, we use the 465 // smaller size style to determine the gap 466 int sizeStyle = Math.min(getSizeStyle(component1), getSizeStyle(component2)); 467 Insets gap1 = getPreferredGap(component1, type, sizeStyle); 468 Insets gap2 = getPreferredGap(component2, type, sizeStyle); 469 470 // The AHIG defines the minimal spacing for a component 471 // therefore we use the larger of the two gap values. 472 switch (position) { 473 case SwingConstants.NORTH : 474 result = Math.max(gap1.top, gap2.bottom); 475 break; 476 case SwingConstants.SOUTH : 477 result = Math.max(gap1.bottom, gap2.top); 478 break; 479 case SwingConstants.EAST : 480 result = Math.max(gap1.right, gap2.left); 481 break; 482 case SwingConstants.WEST : 483 default : 484 result = Math.max(gap1.left, gap2.right); 485 break; 486 } 487 488 // Compensate for visual margin 489 Insets visualMargin1 = getVisualMargin(component1); 490 Insets visualMargin2 = getVisualMargin(component2); 491 492 switch (position) { 493 case SwingConstants.NORTH : 494 result -= visualMargin1.top + visualMargin2.bottom; 495 break; 496 case SwingConstants.SOUTH : 497 result -= visualMargin1.bottom + visualMargin2.top; 498 break; 499 case SwingConstants.EAST : 500 result -= visualMargin1.right + visualMargin2.left; 501 break; 502 case SwingConstants.WEST : 503 result -= visualMargin1.left + visualMargin2.right; 504 default : 505 break; 506 } 507 } 508 509 return result; 510 } 511 getPreferredGap(JComponent component, javax.swing.LayoutStyle.ComponentPlacement type, int sizeStyle)512 private Insets getPreferredGap(JComponent component, javax.swing.LayoutStyle.ComponentPlacement type, int sizeStyle) { 513 Insets gap = null; 514 515 HashMap gapMap; 516 517 switch (type) { 518 case INDENT : 519 gapMap = indentGaps; break; 520 case RELATED : 521 gapMap = relatedGaps; break; 522 case UNRELATED : 523 default : 524 gapMap = unrelatedGaps; break; 525 } 526 527 String uid = component.getUIClassID(); 528 String style = null; 529 if (uid.equals("ButtonUI") || uid.equals("ToggleButtonUI")) { 530 style = (String) component.getClientProperty("Quaqua.Button.style"); 531 if (style == null) { 532 style = (String) component.getClientProperty("JButton.buttonType"); 533 } 534 } else if (uid.equals("ProgressBarUI")) { 535 style = (((JProgressBar) component).getOrientation() == JProgressBar.HORIZONTAL) 536 ? "horizontal" 537 : "vertical"; 538 } else if (uid.equals("SliderUI")) { 539 style = (((JSlider) component).getOrientation() == JSlider.HORIZONTAL) 540 ? "horizontal" 541 : "vertical"; 542 } else if (uid.equals("TabbedPaneUI")) { 543 switch (((JTabbedPane) component).getTabPlacement()) { 544 case JTabbedPane.TOP : style = "top"; break; 545 case JTabbedPane.LEFT : style = "left"; break; 546 case JTabbedPane.BOTTOM : style = "bottom"; break; 547 case JTabbedPane.RIGHT : style = "right"; break; 548 } 549 } 550 String key = (style == null) ? uid : uid+"."+style; 551 Insets[] gaps = (Insets[]) gapMap.get(key); 552 if (gaps == null) { 553 gaps = (Insets[]) gapMap.get(uid); 554 } 555 if (gaps == null) { 556 gaps = (Insets[]) gapMap.get("default"); 557 } 558 559 return (gaps == null) ? new Insets(0,0,0,0) : gaps[sizeStyle]; 560 } 561 562 /** 563 * Returns the amount of space to position a component inside its 564 * parent. 565 * 566 * @param component the <code>Component</code> being positioned 567 * @param position the position <code>component</code> is being placed 568 * relative to its parent; one of 569 * <code>SwingConstants.NORTH</code>, 570 * <code>SwingConstants.SOUTH</code>, 571 * <code>SwingConstants.EAST</code> or 572 * <code>SwingConstants.WEST</code> 573 * @param parent the parent of <code>component</code>; this may differ 574 * from the actual parent and may be null 575 * @return the amount of space to place between the component and specified 576 * edge 577 * @throws IllegalArgumentException if <code>position</code> is not 578 * one of <code>SwingConstants.NORTH</code>, 579 * <code>SwingConstants.SOUTH</code>, 580 * <code>SwingConstants.EAST</code> or 581 * <code>SwingConstants.WEST</code>; 582 * or <code>component</code> is null 583 */ getContainerGap(JComponent component, int position, Container parent)584 public int getContainerGap(JComponent component, int position, 585 Container parent) { 586 int result; 587 588 int sizeStyle = Math.min(getSizeStyle(component), getSizeStyle(parent)); 589 590 // Compute gap 591 Insets gap = getContainerGap(parent, sizeStyle); 592 593 switch (position) { 594 case SwingConstants.NORTH : 595 result = gap.top; 596 break; 597 case SwingConstants.SOUTH : 598 result = gap.bottom; 599 break; 600 case SwingConstants.EAST : 601 result = gap.right; 602 break; 603 case SwingConstants.WEST : 604 default : 605 result = gap.left; 606 break; 607 } 608 609 // Compensate for visual margin 610 Insets visualMargin = getVisualMargin(component); 611 switch (position) { 612 case SwingConstants.NORTH : 613 result -= visualMargin.top; 614 break; 615 case SwingConstants.SOUTH : 616 result -= visualMargin.bottom; 617 // Radio buttons in Quaqua are 1 pixel too high, in order 618 // to align their baselines with other components, when no 619 // baseline aware layout manager is used. 620 if (component instanceof JRadioButton) { 621 result--; 622 } 623 break; 624 case SwingConstants.EAST : 625 result -= visualMargin.left; 626 break; 627 case SwingConstants.WEST : 628 result -= visualMargin.right; 629 default : 630 break; 631 } 632 633 return result; 634 } 635 getContainerGap(Container container, int sizeStyle)636 private Insets getContainerGap(Container container, int sizeStyle) { 637 Insets gap = null; 638 639 HashMap gapMap = containerGaps; 640 641 String uid; 642 if (container instanceof JComponent) { 643 uid = ((JComponent) container).getUIClassID(); 644 } else if (container instanceof Dialog) { 645 uid = "Dialog"; 646 } else if (container instanceof Frame) { 647 uid = "Frame"; 648 } else if (container instanceof java.applet.Applet) { 649 uid = "Applet"; 650 } else if (container instanceof Panel) { 651 uid = "Panel"; 652 } else { 653 uid = "default"; 654 } 655 656 String style = null; 657 // FIXME insert style code here for JInternalFrame with palette style 658 String key = (style == null) ? uid : uid+"."+style; 659 660 Insets[] gaps = (Insets[]) gapMap.get(key); 661 if (gaps == null) { 662 gaps = (Insets[]) gapMap.get(uid); 663 } 664 if (gaps == null) { 665 gaps = (Insets[]) gapMap.get("default"); 666 } 667 if (gaps == null) { 668 if (DEBUG) System.out.println("AquaLayoutStyle.noGaps for "+uid); 669 } 670 return (gaps == null) ? new Insets(0,0,0,0) : gaps[sizeStyle]; 671 } 672 getVisualMargin(JComponent component)673 private Insets getVisualMargin(JComponent component) { 674 try { 675 Method getUI = component.getClass().getMethod("getUI", new Class[0]); 676 Object ui = getUI.invoke(component, new Object[0]); 677 if (ui instanceof VisuallyLayoutable) { 678 Dimension size = component.getPreferredSize(); 679 Rectangle visualBounds = ((VisuallyLayoutable) ui).getVisualBounds(component, VisuallyLayoutable.COMPONENT_BOUNDS, size.width, size.height); 680 return new Insets(visualBounds.y, visualBounds.x, size.height - visualBounds.y - visualBounds.height, size.width - visualBounds.x - visualBounds.width); 681 } 682 } catch (NoSuchMethodException e) { 683 // This can happen for subclasses of JComponent, which do 684 // not have an UI delegate. 685 // Fall through. 686 } catch (Exception e) { 687 InternalError error = new InternalError(); 688 //error.initCause(e); 689 throw error; 690 } 691 return new Insets(0,0,0,0); 692 } 693 getVisualIndent(JComponent component)694 private Insets getVisualIndent(JComponent component) { 695 try { 696 Method getUI = component.getClass().getMethod("getUI", new Class[0]); 697 Object ui = getUI.invoke(component, new Object[0]); 698 if (ui instanceof VisuallyLayoutable) { 699 Dimension size = component.getPreferredSize(); 700 Rectangle visualBounds = ((VisuallyLayoutable) ui).getVisualBounds(component, VisuallyLayoutable.TEXT_BOUNDS, size.width, size.height); 701 return new Insets(visualBounds.y, visualBounds.x, size.height - visualBounds.y - visualBounds.height, size.width - visualBounds.x - visualBounds.width); 702 } 703 } catch (NoSuchMethodException e) { 704 // This can happen for subclasses of JComponent, which do 705 // not have an UI delegate. 706 // Fall through 707 } catch (Exception e) { 708 InternalError error = new InternalError(); 709 // error.initCause(e); 710 throw error; 711 } 712 return new Insets(0,0,0,0); 713 } 714 /** 715 * Returns the size style of a specified component. 716 * 717 * @return REGULAR, SMALL or MINI. 718 */ getSizeStyle(Component c)719 private int getSizeStyle(Component c) { 720 // Aqua components have a different style depending on the 721 // font size used. 722 // 13 Point = Regular 723 // 11 Point = Small 724 // 9 Point = Mini 725 int fontSize = c.getFont().getSize(); 726 return (fontSize >= 13) ? REGULAR : ((fontSize > 9) ? SMALL : MINI); 727 } 728 } 729