All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ch.randelshofer.quaqua.QuaquaLayoutStyle Maven / Gradle / Ivy

Go to download

A Mavenisation of the Quaqua Mac OSX Swing Look and Feel (Java library) Quaqua Look and Feel (C) 2003-2010, Werner Randelshofer. Mavenisation by Matt Gumbley, DevZendo.org - for problems with Mavenisation, see Matt; for issues with Quaqua, see the Quaqua home page. For full license details, see http://randelshofer.ch/quaqua/license.html

The newest version!
/*
 * @(#)QuaquaLayoutStyle.java
 *
 * Copyright (c) 2007-2010 Werner Randelshofer, Immensee, Switzerland.
 * All rights reserved.
 *
 * You may not use, copy or modify this file, except in compliance with the
 * license agreement you entered into with Werner Randelshofer.
 * For details see accompanying license terms.
 */
package ch.randelshofer.quaqua;

import java.awt.*;
import javax.swing.*;
import java.lang.reflect.*;
import java.util.*;

/**
 * A QuaquaLayoutStyle can be queried for the preferred gaps between two
 * JComponents, or between a JComponent and a parent Container.
 *
 * @author  Werner Randelshofer
 * @version $Id: QuaquaLayoutStyle.java 361 2010-11-21 11:19:20Z wrandelshofer $
 */
public class QuaquaLayoutStyle extends LayoutStyle {

    private final static boolean DEBUG = false;
    /** Mini size style. */
    private final static int MINI = 0;
    /** Small size style. */
    private final static int SMALL = 1;
    /** Regular size style. */
    private final static int REGULAR = 2;
    /**
     * The containerGapDefinitions array defines the preferred insets (child gaps)
     * of a parent container towards one of its child components.
     *
     * Note: As of now, we do not yet specify the preferred gap from a child
     * to its parent. Therefore we may not be able to treat all special cases.
     *
     * This array is used to initialize the containerGaps HashMap.
     *
     * The array has the following structure, which is supposed to be a
     * a compromise between legibility and code size.
     * containerGapDefinitions[0..n] = preferred insets for some parent UI's
     * containerGapDefinitions[][0..m-3] = name of parent UI,
     *                                 optionally followed by a full stop and
     *                                 a style name
     * containerGapDefinitions[][m-2] = mini insets
     * containerGapDefinitions[][m-1] = small insets
     * containerGapDefinitions[][m] = regular insets
     */
    private final static Object[][] containerGapDefinitions = {
        // Format:
        // { list of parent UI's,
        //   mini insets, small insets, regular insets }

        {"TabbedPaneUI",
            new Insets(6, 10, 10, 10), new Insets(6, 10, 10, 12), new Insets(12, 20, 20, 20)
        },
        // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGLayout/chapter_19_section_3.html#//apple_ref/doc/uid/TP30000360/DontLinkElementID_27
        // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGLayout/chapter_19_section_3.html#//apple_ref/doc/uid/TP30000360/DontLinkElementID_26
        // note for small and mini size: leave 8 to 10 pixels on top
        // note for regular size: leave only 12 pixel at top if tabbed pane UI
        {"RootPaneUI",
            new Insets(8, 10, 10, 10), new Insets(8, 10, 10, 12), new Insets(14, 20, 20, 20)
        },
        // These child gaps are used for all other components
        {"default",
            new Insets(8, 10, 10, 10), new Insets(8, 10, 10, 12), new Insets(14, 20, 20, 20)
        },};
    /**
     * The relatedGapDefinitions table defines the preferred gaps
     * of one party of two related components.
     *
     * The effective preferred gap is the maximum of the preferred gaps of
     * both parties.
     *
     * This array is used to initialize the relatedGaps HashMap.
     *
     * The array has the following structure, which is supposed to be a
     * a compromise between legibility and code size.
     * containerGapDefinitions[0..n] = preferred gaps for a party of a two related UI's
     * containerGapDefinitions[][0..m-3] = name of UI
     *                                 optionally followed by a full stop and
     *                                 a style name
     * containerGapDefinitions[][m-2] = mini insets
     * containerGapDefinitions[][m-1] = small insets
     * containerGapDefinitions[][m] = regular insets
     */
    private final static Object[][] relatedGapDefinitions = {
        // Format:
        // { list of UI's,
        //   mini insets, small insets, regular insets }

        // Push Button:
        // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF104
        {"ButtonUI", "ButtonUI.push", "ButtonUI.text",
            "ToggleButtonUI.push", "ToggleButtonUI.text",
            new Insets(8, 8, 8, 8), new Insets(10, 10, 10, 10), new Insets(12, 12, 12, 12)
        },
        // Metal Button
        // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF187
        {"ButtonUI.metal", "ToggleButtonUI.metal",
            new Insets(8, 8, 8, 8), new Insets(8, 8, 8, 8), new Insets(12, 12, 12, 12)
        },
        // Bevel Button (Rounded and Square)
        // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF112
        {"ButtonUI.bevel", "ButtonUI.toggle", "ButtonUI.square",
            "ToggleButtonUI", "ToggleButtonUI.bevel", "ToggleButtonUI.square", "ToggleButtonUI.toggle",
            new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0)
        },
        // Bevel Button (Rounded and Square)
        // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF112
        {"ButtonUI.bevel.largeIcon", "ToggleButtonUI.bevel.largeIcon",
            new Insets(8, 8, 8, 8), new Insets(8, 8, 8, 8), new Insets(8, 8, 8, 8)
        },
        // Icon Button
        // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF189
        {"ButtonUI.icon",
            new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0)
        },
        {"ButtonUI.icon.largeIcon",
            new Insets(8, 8, 8, 8), new Insets(8, 8, 8, 8), new Insets(8, 8, 8, 8)
        },
        // Round Button
        // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF191
        {"ButtonUI.round", "ToggleButtonUI.round",
            new Insets(12, 12, 12, 12), new Insets(12, 12, 12, 12), new Insets(12, 12, 12, 12)
        },
        // Help Button
        // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF193
        {"ButtonUI.help",
            new Insets(12, 12, 12, 12), new Insets(12, 12, 12, 12), new Insets(12, 12, 12, 12)
        },
        // Segmented Control
        // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_3.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF196
        {"ButtonUI.toggleCenter", "ToggleButtonUI.toggleCenter",
            new Insets(8, 0, 8, 0), new Insets(10, 0, 10, 0), new Insets(12, 0, 12, 0)
        },
        {"ButtonUI.toggleEast", "ToggleButtonUI.toggleEast",
            new Insets(8, 0, 8, 8), new Insets(10, 0, 10, 10), new Insets(12, 0, 12, 12)
        },
        {"ButtonUI.toggleWest", "ToggleButtonUI.toggleWest",
            new Insets(8, 8, 8, 0), new Insets(10, 10, 10, 0), new Insets(12, 12, 12, 0)
        },
        {"ButtonUI.toolBarTab", "ToggleButtonUI.toolBarTab",
            new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0)
        },
        // Color Well Button
        // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_3.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF213
        {"ButtonUI.colorWell", "ToggleButtonUI.colorWell",
            new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0)
        },
        // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_3.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF198
        // FIXME - The following values are given in the AHIG.
        // In reality, the values further below seem to be more appropriate.
        // Which ones are right?
        //{ "CheckBoxUI", new Insets(7, 5, 7, 5), new Insets(8, 6, 8, 6), new Insets(8, 8, 8, 8) },
        {"CheckBoxUI",
            new Insets(6, 5, 6, 5), new Insets(7, 6, 7, 6), new Insets(7, 6, 7, 6)
        },
        // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_3.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF198
        {"ComboBoxUI",
            new Insets(8, 5, 8, 5), new Insets(10, 6, 10, 6), new Insets(12, 8, 12, 8)
        },
        // There is no spacing given for labels in Apples Guidelines.
        // We use the values here, which is the minimum of the spacing of all
        // other components.
        {"LabelUI",
            new Insets(6, 8, 6, 8), new Insets(6, 8, 6, 8), new Insets(6, 8, 6, 8)
        },
        // ? spacing not given
        {"ListUI",
            new Insets(5, 5, 5, 5), new Insets(6, 6, 6, 6), new Insets(6, 6, 6, 6)
        },
        // ? spacing not given
        {"PanelUI",
            new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0)
        },
        // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_5.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF106
        // ? spacing not given
        {"ProgressBarUI",
            new Insets(8, 8, 8, 8), new Insets(10, 10, 10, 10), new Insets(12, 12, 12, 12)
        },
        // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_3.html#//apple_ref/doc/uid/20000957-TP30000359-BIAHBFAD
        {"RadioButtonUI",
            new Insets(5, 5, 5, 5), new Insets(6, 6, 6, 6), new Insets(6, 6, 6, 6)
        },
        //http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_6.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF114
        // ? spacing not given. We use the same like for text fields
        {"ScrollPaneUI",
            new Insets(6, 8, 6, 8), new Insets(6, 8, 6, 8), new Insets(8, 10, 8, 10)
        },
        //http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_8.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF214
        // ? spacing not given
        //http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGLayout/chapter_19_section_2.html#//apple_ref/doc/uid/20000957-TP30000360-CHDEACGD
        {"SeparatorUI",
            new Insets(8, 8, 8, 8), new Insets(10, 10, 10, 10), new Insets(12, 12, 12, 12)
        },
        // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_4.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF115
        {"SliderUI.horizontal",
            new Insets(6, 8, 6, 8), new Insets(6, 10, 6, 10), new Insets(6, 12, 6, 12)
        },
        {"SliderUI.vertical",
            new Insets(8, 8, 8, 8), new Insets(10, 10, 10, 10), new Insets(12, 12, 12, 12)
        },
        //http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_4.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF204
        {"SpinnerUI",
            new Insets(6, 8, 6, 8), new Insets(6, 8, 6, 8), new Insets(8, 10, 8, 10)
        },
        // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_7.html#//apple_ref/doc/uid/20000957-TP30000359-CHDDBIJE
        // ? spacing not given
        {"SplitPaneUI",
            new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0)
        },
        // http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_7.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF105
        // ? spacing not given
        {"TabbedPaneUI",
            new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0)
        },
        {"TableUI",
            new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0)
        },
        // ? spacing not given
        {"TextAreaUI", "EditorPaneUI", "TextPaneUI",
            new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0)
        },
        //http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_6.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF225
        {"TextFieldUI", "FormattedTextFieldUI", "PasswordFieldUI",
            new Insets(6, 8, 6, 8), new Insets(6, 8, 6, 8), new Insets(8, 10, 8, 10)
        },
        // ? spacing not given
        {"TreeUI",
            new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0)
        },};
    private final static Object[][] unrelatedGapDefinitions = {
        // UI, mini, small, regular
        {"ButtonUI.help",
            new Insets(24, 24, 24, 24), new Insets(24, 24, 24, 24), new Insets(24, 24, 24, 24)
        },
        {"default",
            new Insets(12, 12, 12, 12), new Insets(14, 14, 14, 14), new Insets(16, 16, 16, 16)
        },};
    /**
     * The indentGapDefinitions table defines the preferred indentation
     * for components that are indented after the specified component.
     *
     * This array is used to initialize the indentGaps HashMap.
     *
     * The array has the following structure, which is supposed to be a
     * a compromise between legibility and code size.
     * indentGapDefinitions[0..n] = preferred gaps for a party of a two related UI's
     * indentGapDefinitions[][0..m-3] = name of UI
     *                                 optionally followed by a full stop and
     *                                 a style name
     * indentGapDefinitions[][m-2] = mini insets
     * indentGapDefinitions[][m-1] = small insets
     * indentGapDefinitions[][m] = regular insets
     */
    private final static Object[][] indentGapDefinitions = {
        // UI, mini, small, regular
        {"default",
            new Insets(16, 16, 16, 16), new Insets(20, 20, 20, 20), new Insets(25, 25, 25, 25)
        },};

    /**
     * Creates a hash map for the specified definitions array.
     *
     * Each entry of the hash map has the name of an UI (optionally followed by
     * a full stop and a style name) as its key 
     * and an array of Insets as its value .
     */
    private static HashMap createMap(Object[][] definitions) {
        HashMap map = new HashMap();
        for (int i = 0; i < definitions.length; i++) {
            int keys = 0;
            while (keys < definitions[i].length
                    && (definitions[i][keys] instanceof String)) {
                keys++;
            }
            Insets[] values = new Insets[definitions[i].length - keys];
            for (int j = keys; j < definitions[i].length; j++) {
                values[j - keys] = (Insets) definitions[i][j];
            }
            for (int j = 0; j < keys; j++) {
                map.put(definitions[i][j], values);
            }
        }
        return map;
    }
    /**
     * The relatedGapDefinitions table defines the preferred gaps
     * of one party of two related components.
     */
    private final static HashMap relatedGaps = createMap(relatedGapDefinitions);
    /**
     * The unrelatedGapDefinitions table defines the preferred gaps
     * of one party of two unrelated components.
     */
    private final static HashMap unrelatedGaps = createMap(unrelatedGapDefinitions);
    /**
     * The containerGapDefinitions array defines the preferred insets (child gaps)
     * of a parent component towards one of its children.
     */
    private final static HashMap containerGaps = createMap(containerGapDefinitions);
    /**
     * The indentGapDefinitions table defines the preferred indentation
     * for components that are indented after the specified component.
     */
    private final static HashMap indentGaps = createMap(indentGapDefinitions);

    /**
     * Creates a new instance.
     */
    public QuaquaLayoutStyle() {
    }

    /**
     * Returns the amount of space to use between two components.
     * The return value indicates the distance to place
     * component2 relative to component1.
     * For example, the following returns the amount of space to place
     * between component2 and component1
     * when component2 is placed vertically above
     * component1:
     * 
     *   int gap = getPreferredGap(component1, component2,
     *                             LayoutStyle.RELATED,
     *                             SwingConstants.NORTH, parent);
     * 
* The type parameter indicates the type * of gap being requested. It can be one of the following values: * *
RELATED * If the two components will be contained in * the same parent and are showing similar logically related * items, use RELATED. *
UNRELATED * If the two components will be * contained in the same parent but show logically unrelated items * use UNRELATED. *
INDENT * Used to obtain the preferred distance to indent a component * relative to another. For example, if you want to horizontally * indent a JCheckBox relative to a JLabel use INDENT. * This is only useful for the horizontal axis. *
*

* It's important to note that some look and feels may not distinguish * between RELATED and UNRELATED. *

* The return value is not intended to take into account the * current size and position of component2 or * component1. The return value may take into * consideration various properties of the components. For * example, the space may vary based on font size, or the preferred * size of the component. * * @param component1 the JComponent * component2 is being placed relative to * @param component2 the JComponent being placed * @param type how the two components are being placed * @param position the position component2 is being placed * relative to component1; one of * SwingConstants.NORTH, * SwingConstants.SOUTH, * SwingConstants.EAST or * SwingConstants.WEST * @param parent the parent of component2; this may differ * from the actual parent and may be null * @return the amount of space to place between the two components * @throws IllegalArgumentException if position is not * one of SwingConstants.NORTH, * SwingConstants.SOUTH, * SwingConstants.EAST or * SwingConstants.WEST; type not one * of INDENT, RELATED * or UNRELATED; or component1 or * component2 is null */ public int getPreferredGap(JComponent component1, JComponent component2, javax.swing.LayoutStyle.ComponentPlacement type, int position, Container parent) { int result; if (type == javax.swing.LayoutStyle.ComponentPlacement.INDENT) { // Compute gap int sizeVariant = getSizeVariant(component1); Insets vgap = getVisualIndent(component1); Insets pgap = getPreferredGap(component1, javax.swing.LayoutStyle.ComponentPlacement.INDENT, sizeVariant); switch (position) { case SwingConstants.NORTH: result = (vgap.bottom > 8) ? vgap.bottom : pgap.bottom; break; case SwingConstants.SOUTH: result = (vgap.top > 8) ? vgap.top : pgap.top; break; case SwingConstants.EAST: result = (vgap.left > 8) ? vgap.left : pgap.left; break; case SwingConstants.WEST: default: result = (vgap.right > 8) ? vgap.right : pgap.right; break; } // Compensate for visual margin Insets visualMargin2 = getVisualMargin(component2); switch (position) { case SwingConstants.NORTH: result -= visualMargin2.bottom; break; case SwingConstants.SOUTH: result -= visualMargin2.top; break; case SwingConstants.EAST: result -= visualMargin2.left; break; case SwingConstants.WEST: result -= visualMargin2.right; default: break; } } else { // If the two components have different size styles, we use the // smaller size style to determine the gap int sizeVariant = Math.min(getSizeVariant(component1), getSizeVariant(component2)); Insets gap1 = getPreferredGap(component1, type, sizeVariant); Insets gap2 = getPreferredGap(component2, type, sizeVariant); // The AHIG defines the minimal spacing for a component // therefore we use the larger of the two gap values. switch (position) { case SwingConstants.NORTH: result = Math.max(gap1.top, gap2.bottom); break; case SwingConstants.SOUTH: result = Math.max(gap1.bottom, gap2.top); break; case SwingConstants.EAST: result = Math.max(gap1.right, gap2.left); break; case SwingConstants.WEST: default: result = Math.max(gap1.left, gap2.right); break; } // Compensate for visual margin Insets visualMargin1 = getVisualMargin(component1); Insets visualMargin2 = getVisualMargin(component2); switch (position) { case SwingConstants.NORTH: result -= visualMargin1.top + visualMargin2.bottom; break; case SwingConstants.SOUTH: result -= visualMargin1.bottom + visualMargin2.top; break; case SwingConstants.EAST: result -= visualMargin1.right + visualMargin2.left; break; case SwingConstants.WEST: result -= visualMargin1.left + visualMargin2.right; default: break; } } //System.out.println("QuaquaLayoutStyle.getPreferredGap:"+component1.getClass()+"@"+component1.hashCode()+","+position+","+component2.getClass()+"@"+component2.hashCode()+":"+result); return result; } private Insets getPreferredGap(JComponent component, javax.swing.LayoutStyle.ComponentPlacement type, int sizeStyle) { Insets gap = null; HashMap gapMap; switch (type) { case INDENT: gapMap = indentGaps; break; case RELATED: gapMap = relatedGaps; break; case UNRELATED: default: gapMap = unrelatedGaps; break; } String uid = component.getUIClassID(); String style = null; if (uid.equals("ButtonUI") || uid.equals("ToggleButtonUI")) { style = (String) component.getClientProperty("Quaqua.Button.style"); if (style == null) { style = (String) component.getClientProperty("JButton.buttonType"); } } else if (uid.equals("ProgressBarUI")) { style = (((JProgressBar) component).getOrientation() == JProgressBar.HORIZONTAL) ? "horizontal" : "vertical"; } else if (uid.equals("SliderUI")) { style = (((JSlider) component).getOrientation() == JSlider.HORIZONTAL) ? "horizontal" : "vertical"; } else if (uid.equals("TabbedPaneUI")) { switch (((JTabbedPane) component).getTabPlacement()) { case JTabbedPane.TOP: style = "top"; break; case JTabbedPane.LEFT: style = "left"; break; case JTabbedPane.BOTTOM: style = "bottom"; break; case JTabbedPane.RIGHT: style = "right"; break; } } String key = (style == null) ? uid : uid + "." + style; Insets[] gaps = (Insets[]) gapMap.get(key); if (gaps == null) { gaps = (Insets[]) gapMap.get(uid); } if (gaps == null) { gaps = (Insets[]) gapMap.get("default"); } return (gaps == null) ? new Insets(0, 0, 0, 0) : gaps[sizeStyle]; } /** * Returns the amount of space to position a component inside its * parent. * * @param component the Component being positioned * @param position the position component is being placed * relative to its parent; one of * SwingConstants.NORTH, * SwingConstants.SOUTH, * SwingConstants.EAST or * SwingConstants.WEST * @param parent the parent of component; this may differ * from the actual parent and may be null * @return the amount of space to place between the component and specified * edge * @throws IllegalArgumentException if position is not * one of SwingConstants.NORTH, * SwingConstants.SOUTH, * SwingConstants.EAST or * SwingConstants.WEST; * or component is null */ public int getContainerGap(JComponent component, int position, Container parent) { int result; int sizeVariant = Math.min(getSizeVariant(component), getSizeVariant(parent)); // Compute gap Insets gap = getContainerGap(parent, sizeVariant); switch (position) { case SwingConstants.NORTH: result = gap.top; break; case SwingConstants.SOUTH: result = gap.bottom; break; case SwingConstants.EAST: result = gap.right; break; case SwingConstants.WEST: default: result = gap.left; break; } // Compensate for visual margin Insets visualMargin = getVisualMargin(component); switch (position) { case SwingConstants.NORTH: result -= visualMargin.top; break; case SwingConstants.SOUTH: result -= visualMargin.bottom; // Radio buttons in Quaqua are 1 pixel too high, in order // to align their baselines with other components, when no // baseline aware layout manager is used. if (component instanceof JRadioButton) { result--; } break; case SwingConstants.EAST: result -= visualMargin.left; break; case SwingConstants.WEST: result -= visualMargin.right; default: break; } //System.out.println("QuaquaLayoutStyle.getContainerGap:"+component.getClass()+"@"+component.hashCode()+","+position+","+parent.getClass()+":"+result); return result; } private Insets getContainerGap(Container container, int sizeStyle) { Insets gap = null; HashMap gapMap = containerGaps; String uid; if (container instanceof JComponent) { uid = ((JComponent) container).getUIClassID(); } else if (container instanceof Dialog) { uid = "Dialog"; } else if (container instanceof Frame) { uid = "Frame"; } else if (container instanceof java.applet.Applet) { uid = "Applet"; } else if (container instanceof Panel) { uid = "Panel"; } else { uid = "default"; } String style = null; // FIXME insert style code here for JInternalFrame with palette style String key = (style == null) ? uid : uid + "." + style; Insets[] gaps = (Insets[]) gapMap.get(key); if (gaps == null) { gaps = (Insets[]) gapMap.get(uid); } if (gaps == null) { gaps = (Insets[]) gapMap.get("default"); } if (gaps == null) { if (DEBUG) { System.out.println("AquaLayoutStyle.noGaps for " + uid); } } return (gaps == null) ? new Insets(0, 0, 0, 0) : gaps[sizeStyle]; } private Insets getVisualMargin(JComponent component) { try { Method getUI = component.getClass().getMethod("getUI", new Class[0]); Object ui = getUI.invoke(component, new Object[0]); if (ui instanceof VisuallyLayoutable) { Dimension size = component.getPreferredSize(); Rectangle visualBounds = ((VisuallyLayoutable) ui).getVisualBounds(component, VisuallyLayoutable.COMPONENT_BOUNDS, size.width, size.height); return new Insets(visualBounds.y, visualBounds.x, size.height - visualBounds.y - visualBounds.height, size.width - visualBounds.x - visualBounds.width); } } catch (NoSuchMethodException e) { // This can happen for subclasses of JComponent, which do // not have an UI delegate. // Fall through. } catch (Exception e) { InternalError error = new InternalError(); //error.initCause(e); throw error; } return new Insets(0, 0, 0, 0); } private Insets getVisualIndent(JComponent component) { try { Method getUI = component.getClass().getMethod("getUI", new Class[0]); Object ui = getUI.invoke(component, new Object[0]); if (ui instanceof VisuallyLayoutable) { Dimension size = component.getPreferredSize(); Rectangle visualBounds = ((VisuallyLayoutable) ui).getVisualBounds(component, VisuallyLayoutable.TEXT_BOUNDS, size.width, size.height); return new Insets(visualBounds.y, visualBounds.x, size.height - visualBounds.y - visualBounds.height, size.width - visualBounds.x - visualBounds.width); } } catch (NoSuchMethodException e) { // This can happen for subclasses of JComponent, which do // not have an UI delegate. // Fall through } catch (Exception e) { InternalError error = new InternalError(); // error.initCause(e); throw error; } return new Insets(0, 0, 0, 0); } /** * Returns the size variant of a specified component. * * @return REGULAR, SMALL or MINI. */ private int getSizeVariant(Component c) { // Look for size variant client property if (c instanceof JComponent) { String variant = (String) ((JComponent) c).getClientProperty("JComponent.sizeVariant"); if (variant != null) { if (variant.equals("regular")) { return REGULAR; } if (variant.equals("mini")) { return MINI; } if (variant.equals("small")) { return SMALL; } } } // Aqua components have a different style depending on the // font size used. // 13 Point = Regular // 11 Point = Small // 9 Point = Mini Font f = c.getFont(); if (f==null) return REGULAR; int fontSize = f.getSize(); return (fontSize >= 13) ? REGULAR : ((fontSize > 9) ? SMALL : MINI); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy