Home » openjdk-7 » sun » swing » [javadoc | source]

    1   /*
    2    * Copyright (c) 2002, 2008, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   package sun.swing;
   27   
   28   import static sun.swing.SwingUtilities2.BASICMENUITEMUI_MAX_TEXT_OFFSET;
   29   
   30   import javax.swing;
   31   import javax.swing.plaf.basic.BasicHTML;
   32   import javax.swing.text.View;
   33   import java.awt;
   34   import java.awt.event.KeyEvent;
   35   import java.util.Map;
   36   import java.util.HashMap;
   37   
   38   /**
   39    * Calculates preferred size and layouts menu items.
   40    */
   41   public class MenuItemLayoutHelper {
   42   
   43       /* Client Property keys for calculation of maximal widths */
   44       public static final StringUIClientPropertyKey MAX_ARROW_WIDTH =
   45                           new StringUIClientPropertyKey("maxArrowWidth");
   46       public static final StringUIClientPropertyKey MAX_CHECK_WIDTH =
   47                           new StringUIClientPropertyKey("maxCheckWidth");
   48       public static final StringUIClientPropertyKey MAX_ICON_WIDTH =
   49                           new StringUIClientPropertyKey("maxIconWidth");
   50       public static final StringUIClientPropertyKey MAX_TEXT_WIDTH =
   51                           new StringUIClientPropertyKey("maxTextWidth");
   52       public static final StringUIClientPropertyKey MAX_ACC_WIDTH =
   53                           new StringUIClientPropertyKey("maxAccWidth");
   54       public static final StringUIClientPropertyKey MAX_LABEL_WIDTH =
   55                           new StringUIClientPropertyKey("maxLabelWidth");
   56   
   57       private JMenuItem mi;
   58       private JComponent miParent;
   59   
   60       private Font font;
   61       private Font accFont;
   62       private FontMetrics fm;
   63       private FontMetrics accFm;
   64   
   65       private Icon icon;
   66       private Icon checkIcon;
   67       private Icon arrowIcon;
   68       private String text;
   69       private String accText;
   70   
   71       private boolean isColumnLayout;
   72       private boolean useCheckAndArrow;
   73       private boolean isLeftToRight;
   74       private boolean isTopLevelMenu;
   75       private View htmlView;
   76   
   77       private int verticalAlignment;
   78       private int horizontalAlignment;
   79       private int verticalTextPosition;
   80       private int horizontalTextPosition;
   81       private int gap;
   82       private int leadingGap;
   83       private int afterCheckIconGap;
   84       private int minTextOffset;
   85   
   86       private int leftTextExtraWidth;
   87   
   88       private Rectangle viewRect;
   89   
   90       private RectSize iconSize;
   91       private RectSize textSize;
   92       private RectSize accSize;
   93       private RectSize checkSize;
   94       private RectSize arrowSize;
   95       private RectSize labelSize;
   96   
   97       /**
   98        * The empty protected constructor is necessary for derived classes.
   99        */
  100       protected MenuItemLayoutHelper() {
  101       }
  102   
  103       public MenuItemLayoutHelper(JMenuItem mi, Icon checkIcon, Icon arrowIcon,
  104                         Rectangle viewRect, int gap, String accDelimiter,
  105                         boolean isLeftToRight, Font font, Font accFont,
  106                         boolean useCheckAndArrow, String propertyPrefix) {
  107           reset(mi, checkIcon, arrowIcon, viewRect, gap, accDelimiter,
  108                 isLeftToRight, font, accFont, useCheckAndArrow, propertyPrefix);
  109       }
  110   
  111       protected void reset(JMenuItem mi, Icon checkIcon, Icon arrowIcon,
  112                         Rectangle viewRect, int gap, String accDelimiter,
  113                         boolean isLeftToRight, Font font, Font accFont,
  114                         boolean useCheckAndArrow, String propertyPrefix) {
  115           this.mi = mi;
  116           this.miParent = getMenuItemParent(mi);
  117           this.accText = getAccText(accDelimiter);
  118           this.verticalAlignment = mi.getVerticalAlignment();
  119           this.horizontalAlignment = mi.getHorizontalAlignment();
  120           this.verticalTextPosition = mi.getVerticalTextPosition();
  121           this.horizontalTextPosition = mi.getHorizontalTextPosition();
  122           this.useCheckAndArrow = useCheckAndArrow;
  123           this.font = font;
  124           this.accFont = accFont;
  125           this.fm = mi.getFontMetrics(font);
  126           this.accFm = mi.getFontMetrics(accFont);
  127           this.isLeftToRight = isLeftToRight;
  128           this.isColumnLayout = isColumnLayout(isLeftToRight,
  129                   horizontalAlignment, horizontalTextPosition,
  130                   verticalTextPosition);
  131           this.isTopLevelMenu = (this.miParent == null) ? true : false;
  132           this.checkIcon = checkIcon;
  133           this.icon = getIcon(propertyPrefix);
  134           this.arrowIcon = arrowIcon;
  135           this.text = mi.getText();
  136           this.gap = gap;
  137           this.afterCheckIconGap = getAfterCheckIconGap(propertyPrefix);
  138           this.minTextOffset = getMinTextOffset(propertyPrefix);
  139           this.htmlView = (View) mi.getClientProperty(BasicHTML.propertyKey);
  140           this.viewRect = viewRect;
  141   
  142           this.iconSize = new RectSize();
  143           this.textSize = new RectSize();
  144           this.accSize = new RectSize();
  145           this.checkSize = new RectSize();
  146           this.arrowSize = new RectSize();
  147           this.labelSize = new RectSize();
  148           calcExtraWidths();
  149           calcWidthsAndHeights();
  150           setOriginalWidths();
  151           calcMaxWidths();
  152   
  153           this.leadingGap = getLeadingGap(propertyPrefix);
  154           calcMaxTextOffset(viewRect);
  155       }
  156   
  157       private void calcExtraWidths() {
  158           leftTextExtraWidth = getLeftExtraWidth(text);
  159       }
  160   
  161       private int getLeftExtraWidth(String str) {
  162           int lsb = SwingUtilities2.getLeftSideBearing(mi, fm, str);
  163           if (lsb < 0) {
  164               return -lsb;
  165           } else {
  166               return 0;
  167           }
  168       }
  169   
  170       private void setOriginalWidths() {
  171           iconSize.origWidth = iconSize.width;
  172           textSize.origWidth = textSize.width;
  173           accSize.origWidth = accSize.width;
  174           checkSize.origWidth = checkSize.width;
  175           arrowSize.origWidth = arrowSize.width;
  176       }
  177   
  178       private String getAccText(String acceleratorDelimiter) {
  179           String accText = "";
  180           KeyStroke accelerator = mi.getAccelerator();
  181           if (accelerator != null) {
  182               int modifiers = accelerator.getModifiers();
  183               if (modifiers > 0) {
  184                   accText = KeyEvent.getKeyModifiersText(modifiers);
  185                   accText += acceleratorDelimiter;
  186               }
  187               int keyCode = accelerator.getKeyCode();
  188               if (keyCode != 0) {
  189                   accText += KeyEvent.getKeyText(keyCode);
  190               } else {
  191                   accText += accelerator.getKeyChar();
  192               }
  193           }
  194           return accText;
  195       }
  196   
  197       private Icon getIcon(String propertyPrefix) {
  198           // In case of column layout, .checkIconFactory is defined for this UI,
  199           // the icon is compatible with it and useCheckAndArrow() is true,
  200           // then the icon is handled by the checkIcon.
  201           Icon icon = null;
  202           MenuItemCheckIconFactory iconFactory =
  203                   (MenuItemCheckIconFactory) UIManager.get(propertyPrefix
  204                           + ".checkIconFactory");
  205           if (!isColumnLayout || !useCheckAndArrow || iconFactory == null
  206                   || !iconFactory.isCompatible(checkIcon, propertyPrefix)) {
  207               icon = mi.getIcon();
  208           }
  209           return icon;
  210       }
  211   
  212       private int getMinTextOffset(String propertyPrefix) {
  213           int minimumTextOffset = 0;
  214           Object minimumTextOffsetObject =
  215                   UIManager.get(propertyPrefix + ".minimumTextOffset");
  216           if (minimumTextOffsetObject instanceof Integer) {
  217               minimumTextOffset = (Integer) minimumTextOffsetObject;
  218           }
  219           return minimumTextOffset;
  220       }
  221   
  222       private int getAfterCheckIconGap(String propertyPrefix) {
  223           int afterCheckIconGap = gap;
  224           Object afterCheckIconGapObject =
  225                   UIManager.get(propertyPrefix + ".afterCheckIconGap");
  226           if (afterCheckIconGapObject instanceof Integer) {
  227               afterCheckIconGap = (Integer) afterCheckIconGapObject;
  228           }
  229           return afterCheckIconGap;
  230       }
  231   
  232       private int getLeadingGap(String propertyPrefix) {
  233           if (checkSize.getMaxWidth() > 0) {
  234               return getCheckOffset(propertyPrefix);
  235           } else {
  236               return gap; // There is no any check icon
  237           }
  238       }
  239   
  240       private int getCheckOffset(String propertyPrefix) {
  241           int checkIconOffset = gap;
  242           Object checkIconOffsetObject =
  243                   UIManager.get(propertyPrefix + ".checkIconOffset");
  244           if (checkIconOffsetObject instanceof Integer) {
  245               checkIconOffset = (Integer) checkIconOffsetObject;
  246           }
  247           return checkIconOffset;
  248       }
  249   
  250       protected void calcWidthsAndHeights() {
  251           // iconRect
  252           if (icon != null) {
  253               iconSize.width = icon.getIconWidth();
  254               iconSize.height = icon.getIconHeight();
  255           }
  256   
  257           // accRect
  258           if (!accText.equals("")) {
  259               accSize.width = SwingUtilities2.stringWidth(mi, accFm, accText);
  260               accSize.height = accFm.getHeight();
  261           }
  262   
  263           // textRect
  264           if (text == null) {
  265               text = "";
  266           } else if (!text.equals("")) {
  267               if (htmlView != null) {
  268                   // Text is HTML
  269                   textSize.width =
  270                           (int) htmlView.getPreferredSpan(View.X_AXIS);
  271                   textSize.height =
  272                           (int) htmlView.getPreferredSpan(View.Y_AXIS);
  273               } else {
  274                   // Text isn't HTML
  275                   textSize.width = SwingUtilities2.stringWidth(mi, fm, text);
  276                   textSize.height = fm.getHeight();
  277               }
  278           }
  279   
  280           if (useCheckAndArrow) {
  281               // checkIcon
  282               if (checkIcon != null) {
  283                   checkSize.width = checkIcon.getIconWidth();
  284                   checkSize.height = checkIcon.getIconHeight();
  285               }
  286               // arrowRect
  287               if (arrowIcon != null) {
  288                   arrowSize.width = arrowIcon.getIconWidth();
  289                   arrowSize.height = arrowIcon.getIconHeight();
  290               }
  291           }
  292   
  293           // labelRect
  294           if (isColumnLayout) {
  295               labelSize.width = iconSize.width + textSize.width + gap;
  296               labelSize.height = max(checkSize.height, iconSize.height,
  297                       textSize.height, accSize.height, arrowSize.height);
  298           } else {
  299               Rectangle textRect = new Rectangle();
  300               Rectangle iconRect = new Rectangle();
  301               SwingUtilities.layoutCompoundLabel(mi, fm, text, icon,
  302                       verticalAlignment, horizontalAlignment,
  303                       verticalTextPosition, horizontalTextPosition,
  304                       viewRect, iconRect, textRect, gap);
  305               textRect.width += leftTextExtraWidth;
  306               Rectangle labelRect = iconRect.union(textRect);
  307               labelSize.height = labelRect.height;
  308               labelSize.width = labelRect.width;
  309           }
  310       }
  311   
  312       protected void calcMaxWidths() {
  313           calcMaxWidth(checkSize, MAX_CHECK_WIDTH);
  314           calcMaxWidth(arrowSize, MAX_ARROW_WIDTH);
  315           calcMaxWidth(accSize, MAX_ACC_WIDTH);
  316   
  317           if (isColumnLayout) {
  318               calcMaxWidth(iconSize, MAX_ICON_WIDTH);
  319               calcMaxWidth(textSize, MAX_TEXT_WIDTH);
  320               int curGap = gap;
  321               if ((iconSize.getMaxWidth() == 0)
  322                       || (textSize.getMaxWidth() == 0)) {
  323                   curGap = 0;
  324               }
  325               labelSize.maxWidth =
  326                       calcMaxValue(MAX_LABEL_WIDTH, iconSize.maxWidth
  327                               + textSize.maxWidth + curGap);
  328           } else {
  329               // We shouldn't use current icon and text widths
  330               // in maximal widths calculation for complex layout.
  331               iconSize.maxWidth = getParentIntProperty(MAX_ICON_WIDTH);
  332               calcMaxWidth(labelSize, MAX_LABEL_WIDTH);
  333               // If maxLabelWidth is wider
  334               // than the widest icon + the widest text + gap,
  335               // we should update the maximal text witdh
  336               int candidateTextWidth = labelSize.maxWidth - iconSize.maxWidth;
  337               if (iconSize.maxWidth > 0) {
  338                   candidateTextWidth -= gap;
  339               }
  340               textSize.maxWidth = calcMaxValue(MAX_TEXT_WIDTH, candidateTextWidth);
  341           }
  342       }
  343   
  344       protected void calcMaxWidth(RectSize rs, Object key) {
  345           rs.maxWidth = calcMaxValue(key, rs.width);
  346       }
  347   
  348       /**
  349        * Calculates and returns maximal value through specified parent component
  350        * client property.
  351        *
  352        * @param propertyName name of the property, which stores the maximal value.
  353        * @param value a value which pretends to be maximal
  354        * @return maximal value among the parent property and the value.
  355        */
  356       protected int calcMaxValue(Object propertyName, int value) {
  357           // Get maximal value from parent client property
  358           int maxValue = getParentIntProperty(propertyName);
  359           // Store new maximal width in parent client property
  360           if (value > maxValue) {
  361               if (miParent != null) {
  362                   miParent.putClientProperty(propertyName, value);
  363               }
  364               return value;
  365           } else {
  366               return maxValue;
  367           }
  368       }
  369   
  370       /**
  371        * Returns parent client property as int.
  372        * @param propertyName name of the parent property.
  373        * @return value of the property as int.
  374        */
  375       protected int getParentIntProperty(Object propertyName) {
  376           Object value = null;
  377           if (miParent != null) {
  378               value = miParent.getClientProperty(propertyName);
  379           }
  380           if ((value == null) || !(value instanceof Integer)) {
  381               value = 0;
  382           }
  383           return (Integer) value;
  384       }
  385   
  386       public static boolean isColumnLayout(boolean isLeftToRight,
  387                                            JMenuItem mi) {
  388           assert(mi != null);
  389           return isColumnLayout(isLeftToRight, mi.getHorizontalAlignment(),
  390                   mi.getHorizontalTextPosition(), mi.getVerticalTextPosition());
  391       }
  392   
  393       /**
  394        * Answers should we do column layout for a menu item or not.
  395        * We do it when a user doesn't set any alignments
  396        * and text positions manually, except the vertical alignment.
  397        */
  398       public static boolean isColumnLayout(boolean isLeftToRight,
  399                                            int horizontalAlignment,
  400                                            int horizontalTextPosition,
  401                                            int verticalTextPosition) {
  402           if (verticalTextPosition != SwingConstants.CENTER) {
  403               return false;
  404           }
  405           if (isLeftToRight) {
  406               if (horizontalAlignment != SwingConstants.LEADING
  407                       && horizontalAlignment != SwingConstants.LEFT) {
  408                   return false;
  409               }
  410               if (horizontalTextPosition != SwingConstants.TRAILING
  411                       && horizontalTextPosition != SwingConstants.RIGHT) {
  412                   return false;
  413               }
  414           } else {
  415               if (horizontalAlignment != SwingConstants.LEADING
  416                       && horizontalAlignment != SwingConstants.RIGHT) {
  417                   return false;
  418               }
  419               if (horizontalTextPosition != SwingConstants.TRAILING
  420                       && horizontalTextPosition != SwingConstants.LEFT) {
  421                   return false;
  422               }
  423           }
  424           return true;
  425       }
  426   
  427       /**
  428        * Calculates maximal text offset.
  429        * It is required for some L&Fs (ex: Vista L&F).
  430        * The offset is meaningful only for L2R column layout.
  431        *
  432        * @param viewRect the rectangle, the maximal text offset
  433        * will be calculated for.
  434        */
  435       private void calcMaxTextOffset(Rectangle viewRect) {
  436           if (!isColumnLayout || !isLeftToRight) {
  437               return;
  438           }
  439   
  440           // Calculate the current text offset
  441           int offset = viewRect.x + leadingGap + checkSize.maxWidth
  442                   + afterCheckIconGap + iconSize.maxWidth + gap;
  443           if (checkSize.maxWidth == 0) {
  444               offset -= afterCheckIconGap;
  445           }
  446           if (iconSize.maxWidth == 0) {
  447               offset -= gap;
  448           }
  449   
  450           // maximal text offset shouldn't be less than minimal text offset;
  451           if (offset < minTextOffset) {
  452               offset = minTextOffset;
  453           }
  454   
  455           // Calculate and store the maximal text offset
  456           calcMaxValue(SwingUtilities2.BASICMENUITEMUI_MAX_TEXT_OFFSET, offset);
  457       }
  458   
  459       /**
  460        * Layout icon, text, check icon, accelerator text and arrow icon
  461        * in the viewRect and return their positions.
  462        *
  463        * If horizontalAlignment, verticalTextPosition and horizontalTextPosition
  464        * are default (user doesn't set any manually) the layouting algorithm is:
  465        * Elements are layouted in the five columns:
  466        * check icon + icon + text + accelerator text + arrow icon
  467        *
  468        * In the other case elements are layouted in the four columns:
  469        * check icon + label + accelerator text + arrow icon
  470        * Label is union of icon and text.
  471        *
  472        * The order of columns can be reversed.
  473        * It depends on the menu item orientation.
  474        */
  475       public LayoutResult layoutMenuItem() {
  476           LayoutResult lr = createLayoutResult();
  477           prepareForLayout(lr);
  478   
  479           if (isColumnLayout()) {
  480               if (isLeftToRight()) {
  481                   doLTRColumnLayout(lr, getLTRColumnAlignment());
  482               } else {
  483                   doRTLColumnLayout(lr, getRTLColumnAlignment());
  484               }
  485           } else {
  486               if (isLeftToRight()) {
  487                   doLTRComplexLayout(lr, getLTRColumnAlignment());
  488               } else {
  489                   doRTLComplexLayout(lr, getRTLColumnAlignment());
  490               }
  491           }
  492   
  493           alignAccCheckAndArrowVertically(lr);
  494           return lr;
  495       }
  496   
  497       private LayoutResult createLayoutResult() {
  498           return new LayoutResult(
  499                   new Rectangle(iconSize.width, iconSize.height),
  500                   new Rectangle(textSize.width, textSize.height),
  501                   new Rectangle(accSize.width,  accSize.height),
  502                   new Rectangle(checkSize.width, checkSize.height),
  503                   new Rectangle(arrowSize.width, arrowSize.height),
  504                   new Rectangle(labelSize.width, labelSize.height)
  505           );
  506       }
  507   
  508       public ColumnAlignment getLTRColumnAlignment() {
  509           return ColumnAlignment.LEFT_ALIGNMENT;
  510       }
  511   
  512       public ColumnAlignment getRTLColumnAlignment() {
  513           return ColumnAlignment.RIGHT_ALIGNMENT;
  514       }
  515   
  516       protected void prepareForLayout(LayoutResult lr) {
  517           lr.checkRect.width = checkSize.maxWidth;
  518           lr.accRect.width = accSize.maxWidth;
  519           lr.arrowRect.width = arrowSize.maxWidth;
  520       }
  521   
  522       /**
  523        * Aligns the accelertor text and the check and arrow icons vertically
  524        * with the center of the label rect.
  525        */
  526       private void alignAccCheckAndArrowVertically(LayoutResult lr) {
  527           lr.accRect.y = (int)(lr.labelRect.y
  528                   + (float)lr.labelRect.height/2
  529                   - (float)lr.accRect.height/2);
  530           fixVerticalAlignment(lr, lr.accRect);
  531           if (useCheckAndArrow) {
  532               lr.arrowRect.y = (int)(lr.labelRect.y
  533                       + (float)lr.labelRect.height/2
  534                       - (float)lr.arrowRect.height/2);
  535               lr.checkRect.y = (int)(lr.labelRect.y
  536                       + (float)lr.labelRect.height/2
  537                       - (float)lr.checkRect.height/2);
  538               fixVerticalAlignment(lr, lr.arrowRect);
  539               fixVerticalAlignment(lr, lr.checkRect);
  540           }
  541       }
  542   
  543       /**
  544        * Fixes vertical alignment of all menu item elements if rect.y
  545        * or (rect.y + rect.height) is out of viewRect bounds
  546        */
  547       private void fixVerticalAlignment(LayoutResult lr, Rectangle r) {
  548           int delta = 0;
  549           if (r.y < viewRect.y) {
  550               delta = viewRect.y - r.y;
  551           } else if (r.y + r.height > viewRect.y + viewRect.height) {
  552               delta = viewRect.y + viewRect.height - r.y - r.height;
  553           }
  554           if (delta != 0) {
  555               lr.checkRect.y += delta;
  556               lr.iconRect.y += delta;
  557               lr.textRect.y += delta;
  558               lr.accRect.y += delta;
  559               lr.arrowRect.y += delta;
  560               lr.labelRect.y += delta;
  561           }
  562       }
  563   
  564       private void doLTRColumnLayout(LayoutResult lr, ColumnAlignment alignment) {
  565           // Set maximal width for all the five basic rects
  566           // (three other ones are already maximal)
  567           lr.iconRect.width = iconSize.maxWidth;
  568           lr.textRect.width = textSize.maxWidth;
  569   
  570           // Set X coordinates
  571           // All rects will be aligned at the left side
  572           calcXPositionsLTR(viewRect.x, leadingGap, gap, lr.checkRect,
  573                   lr.iconRect, lr.textRect);
  574   
  575           // Tune afterCheckIconGap
  576           if (lr.checkRect.width > 0) { // there is the afterCheckIconGap
  577               lr.iconRect.x += afterCheckIconGap - gap;
  578               lr.textRect.x += afterCheckIconGap - gap;
  579           }
  580   
  581           calcXPositionsRTL(viewRect.x + viewRect.width, leadingGap, gap,
  582                   lr.arrowRect, lr.accRect);
  583   
  584           // Take into account minimal text offset
  585           int textOffset = lr.textRect.x - viewRect.x;
  586           if (!isTopLevelMenu && (textOffset < minTextOffset)) {
  587               lr.textRect.x += minTextOffset - textOffset;
  588           }
  589   
  590           alignRects(lr, alignment);
  591   
  592           // Set Y coordinate for text and icon.
  593           // Y coordinates for other rects
  594           // will be calculated later in layoutMenuItem.
  595           calcTextAndIconYPositions(lr);
  596   
  597           // Calculate valid X and Y coordinates for labelRect
  598           lr.setLabelRect(lr.textRect.union(lr.iconRect));
  599       }
  600   
  601       private void doLTRComplexLayout(LayoutResult lr, ColumnAlignment alignment) {
  602           lr.labelRect.width = labelSize.maxWidth;
  603   
  604           // Set X coordinates
  605           calcXPositionsLTR(viewRect.x, leadingGap, gap, lr.checkRect,
  606                   lr.labelRect);
  607   
  608           // Tune afterCheckIconGap
  609           if (lr.checkRect.width > 0) { // there is the afterCheckIconGap
  610               lr.labelRect.x += afterCheckIconGap - gap;
  611           }
  612   
  613           calcXPositionsRTL(viewRect.x + viewRect.width,
  614                   leadingGap, gap, lr.arrowRect, lr.accRect);
  615   
  616           // Take into account minimal text offset
  617           int labelOffset = lr.labelRect.x - viewRect.x;
  618           if (!isTopLevelMenu && (labelOffset < minTextOffset)) {
  619               lr.labelRect.x += minTextOffset - labelOffset;
  620           }
  621   
  622           alignRects(lr, alignment);
  623   
  624           // Center labelRect vertically
  625           calcLabelYPosition(lr);
  626   
  627           layoutIconAndTextInLabelRect(lr);
  628       }
  629   
  630       private void doRTLColumnLayout(LayoutResult lr, ColumnAlignment alignment) {
  631           // Set maximal width for all the five basic rects
  632           // (three other ones are already maximal)
  633           lr.iconRect.width = iconSize.maxWidth;
  634           lr.textRect.width = textSize.maxWidth;
  635   
  636           // Set X coordinates
  637           calcXPositionsRTL(viewRect.x + viewRect.width, leadingGap, gap,
  638                   lr.checkRect, lr.iconRect, lr.textRect);
  639   
  640           // Tune the gap after check icon
  641           if (lr.checkRect.width > 0) { // there is the gap after check icon
  642               lr.iconRect.x -= afterCheckIconGap - gap;
  643               lr.textRect.x -= afterCheckIconGap - gap;
  644           }
  645   
  646           calcXPositionsLTR(viewRect.x, leadingGap, gap, lr.arrowRect,
  647                   lr.accRect);
  648   
  649           // Take into account minimal text offset
  650           int textOffset = (viewRect.x + viewRect.width)
  651                          - (lr.textRect.x + lr.textRect.width);
  652           if (!isTopLevelMenu && (textOffset < minTextOffset)) {
  653               lr.textRect.x -= minTextOffset - textOffset;
  654           }
  655   
  656           alignRects(lr, alignment);
  657   
  658           // Set Y coordinates for text and icon.
  659           // Y coordinates for other rects
  660           // will be calculated later in layoutMenuItem.
  661           calcTextAndIconYPositions(lr);
  662   
  663           // Calculate valid X and Y coordinate for labelRect
  664           lr.setLabelRect(lr.textRect.union(lr.iconRect));
  665       }
  666   
  667       private void doRTLComplexLayout(LayoutResult lr, ColumnAlignment alignment) {
  668           lr.labelRect.width = labelSize.maxWidth;
  669   
  670           // Set X coordinates
  671           calcXPositionsRTL(viewRect.x + viewRect.width, leadingGap, gap,
  672                   lr.checkRect, lr.labelRect);
  673   
  674           // Tune the gap after check icon
  675           if (lr.checkRect.width > 0) { // there is the gap after check icon
  676               lr.labelRect.x -= afterCheckIconGap - gap;
  677           }
  678   
  679           calcXPositionsLTR(viewRect.x, leadingGap, gap, lr.arrowRect, lr.accRect);
  680   
  681           // Take into account minimal text offset
  682           int labelOffset = (viewRect.x + viewRect.width)
  683                           - (lr.labelRect.x + lr.labelRect.width);
  684           if (!isTopLevelMenu && (labelOffset < minTextOffset)) {
  685               lr.labelRect.x -= minTextOffset - labelOffset;
  686           }
  687   
  688           alignRects(lr, alignment);
  689   
  690           // Center labelRect vertically
  691           calcLabelYPosition(lr);
  692   
  693           layoutIconAndTextInLabelRect(lr);
  694       }
  695   
  696       private void alignRects(LayoutResult lr, ColumnAlignment alignment) {
  697           alignRect(lr.checkRect, alignment.getCheckAlignment(),
  698                     checkSize.getOrigWidth());
  699           alignRect(lr.iconRect, alignment.getIconAlignment(),
  700                     iconSize.getOrigWidth());
  701           alignRect(lr.textRect, alignment.getTextAlignment(),
  702                     textSize.getOrigWidth());
  703           alignRect(lr.accRect, alignment.getAccAlignment(),
  704                     accSize.getOrigWidth());
  705           alignRect(lr.arrowRect, alignment.getArrowAlignment(),
  706                     arrowSize.getOrigWidth());
  707       }
  708   
  709       private void alignRect(Rectangle rect, int alignment, int origWidth) {
  710           if (alignment == SwingConstants.RIGHT) {
  711               rect.x = rect.x + rect.width - origWidth;
  712           }
  713           rect.width = origWidth;
  714       }
  715   
  716       protected void layoutIconAndTextInLabelRect(LayoutResult lr) {
  717           lr.setTextRect(new Rectangle());
  718           lr.setIconRect(new Rectangle());
  719           SwingUtilities.layoutCompoundLabel(
  720                   mi, fm, text,icon, verticalAlignment, horizontalAlignment,
  721                   verticalTextPosition, horizontalTextPosition, lr.labelRect,
  722                   lr.iconRect, lr.textRect, gap);
  723       }
  724   
  725       private void calcXPositionsLTR(int startXPos, int leadingGap,
  726                                      int gap, Rectangle... rects) {
  727           int curXPos = startXPos + leadingGap;
  728           for (Rectangle rect : rects) {
  729               rect.x = curXPos;
  730               if (rect.width > 0) {
  731                   curXPos += rect.width + gap;
  732               }
  733           }
  734       }
  735   
  736       private void calcXPositionsRTL(int startXPos, int leadingGap,
  737                                      int gap, Rectangle... rects) {
  738           int curXPos = startXPos - leadingGap;
  739           for (Rectangle rect : rects) {
  740               rect.x = curXPos - rect.width;
  741               if (rect.width > 0) {
  742                   curXPos -= rect.width + gap;
  743               }
  744           }
  745       }
  746   
  747      /**
  748        * Sets Y coordinates of text and icon
  749        * taking into account the vertical alignment
  750        */
  751       private void calcTextAndIconYPositions(LayoutResult lr) {
  752           if (verticalAlignment == SwingUtilities.TOP) {
  753               lr.textRect.y  = (int)(viewRect.y
  754                       + (float)lr.labelRect.height/2
  755                       - (float)lr.textRect.height/2);
  756               lr.iconRect.y  = (int)(viewRect.y
  757                       + (float)lr.labelRect.height/2
  758                       - (float)lr.iconRect.height/2);
  759           } else if (verticalAlignment == SwingUtilities.CENTER) {
  760               lr.textRect.y = (int)(viewRect.y
  761                       + (float)viewRect.height/2
  762                       - (float)lr.textRect.height/2);
  763               lr.iconRect.y = (int)(viewRect.y
  764                       + (float)viewRect.height/2
  765                       - (float)lr.iconRect.height/2);
  766           }
  767           else if (verticalAlignment == SwingUtilities.BOTTOM) {
  768               lr.textRect.y = (int)(viewRect.y
  769                       + viewRect.height
  770                       - (float)lr.labelRect.height/2
  771                       - (float)lr.textRect.height/2);
  772               lr.iconRect.y = (int)(viewRect.y
  773                       + viewRect.height
  774                       - (float)lr.labelRect.height/2
  775                       - (float)lr.iconRect.height/2);
  776           }
  777       }
  778   
  779       /**
  780        * Sets labelRect Y coordinate
  781        * taking into account the vertical alignment
  782        */
  783       private void calcLabelYPosition(LayoutResult lr) {
  784           if (verticalAlignment == SwingUtilities.TOP) {
  785               lr.labelRect.y  = viewRect.y;
  786           } else if (verticalAlignment == SwingUtilities.CENTER) {
  787               lr.labelRect.y = (int)(viewRect.y
  788                       + (float)viewRect.height/2
  789                       - (float)lr.labelRect.height/2);
  790           } else if (verticalAlignment == SwingUtilities.BOTTOM) {
  791               lr.labelRect.y  = viewRect.y + viewRect.height
  792                       - lr.labelRect.height;
  793           }
  794       }
  795   
  796       /**
  797        * Returns parent of this component if it is not a top-level menu
  798        * Otherwise returns null.
  799        * @param menuItem the menu item whose parent will be returned.
  800        * @return parent of this component if it is not a top-level menu
  801        * Otherwise returns null.
  802        */
  803       public static JComponent getMenuItemParent(JMenuItem menuItem) {
  804           Container parent = menuItem.getParent();
  805           if ((parent instanceof JComponent) &&
  806                (!(menuItem instanceof JMenu) ||
  807                  !((JMenu)menuItem).isTopLevelMenu())) {
  808               return (JComponent) parent;
  809           } else {
  810               return null;
  811           }
  812       }
  813   
  814       public static void clearUsedParentClientProperties(JMenuItem menuItem) {
  815           clearUsedClientProperties(getMenuItemParent(menuItem));
  816       }
  817   
  818       public static void clearUsedClientProperties(JComponent c) {
  819           if (c != null) {
  820               c.putClientProperty(MAX_ARROW_WIDTH, null);
  821               c.putClientProperty(MAX_CHECK_WIDTH, null);
  822               c.putClientProperty(MAX_ACC_WIDTH, null);
  823               c.putClientProperty(MAX_TEXT_WIDTH, null);
  824               c.putClientProperty(MAX_ICON_WIDTH, null);
  825               c.putClientProperty(MAX_LABEL_WIDTH, null);
  826               c.putClientProperty(BASICMENUITEMUI_MAX_TEXT_OFFSET, null);
  827           }
  828       }
  829   
  830       /**
  831        * Finds and returns maximal integer value in the given array.
  832        * @param values array where the search will be performed.
  833        * @return maximal vaule.
  834        */
  835       public static int max(int... values) {
  836           int maxValue = Integer.MIN_VALUE;
  837           for (int i : values) {
  838               if (i > maxValue) {
  839                   maxValue = i;
  840               }
  841           }
  842           return maxValue;
  843       }
  844   
  845       public static Rectangle createMaxRect() {
  846           return new Rectangle(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
  847       }
  848   
  849       public static void addMaxWidth(RectSize size, int gap, Dimension result) {
  850           if (size.maxWidth > 0) {
  851               result.width += size.maxWidth + gap;
  852           }
  853       }
  854   
  855       public static void addWidth(int width, int gap, Dimension result) {
  856           if (width > 0) {
  857               result.width += width + gap;
  858           }
  859       }
  860   
  861       public JMenuItem getMenuItem() {
  862           return mi;
  863       }
  864   
  865       public JComponent getMenuItemParent() {
  866           return miParent;
  867       }
  868   
  869       public Font getFont() {
  870           return font;
  871       }
  872   
  873       public Font getAccFont() {
  874           return accFont;
  875       }
  876   
  877       public FontMetrics getFontMetrics() {
  878           return fm;
  879       }
  880   
  881       public FontMetrics getAccFontMetrics() {
  882           return accFm;
  883       }
  884   
  885       public Icon getIcon() {
  886           return icon;
  887       }
  888   
  889       public Icon getCheckIcon() {
  890           return checkIcon;
  891       }
  892   
  893       public Icon getArrowIcon() {
  894           return arrowIcon;
  895       }
  896   
  897       public String getText() {
  898           return text;
  899       }
  900   
  901       public String getAccText() {
  902           return accText;
  903       }
  904   
  905       public boolean isColumnLayout() {
  906           return isColumnLayout;
  907       }
  908   
  909       public boolean useCheckAndArrow() {
  910           return useCheckAndArrow;
  911       }
  912   
  913       public boolean isLeftToRight() {
  914           return isLeftToRight;
  915       }
  916   
  917       public boolean isTopLevelMenu() {
  918           return isTopLevelMenu;
  919       }
  920   
  921       public View getHtmlView() {
  922           return htmlView;
  923       }
  924   
  925       public int getVerticalAlignment() {
  926           return verticalAlignment;
  927       }
  928   
  929       public int getHorizontalAlignment() {
  930           return horizontalAlignment;
  931       }
  932   
  933       public int getVerticalTextPosition() {
  934           return verticalTextPosition;
  935       }
  936   
  937       public int getHorizontalTextPosition() {
  938           return horizontalTextPosition;
  939       }
  940   
  941       public int getGap() {
  942           return gap;
  943       }
  944   
  945       public int getLeadingGap() {
  946           return leadingGap;
  947       }
  948   
  949       public int getAfterCheckIconGap() {
  950           return afterCheckIconGap;
  951       }
  952   
  953       public int getMinTextOffset() {
  954           return minTextOffset;
  955       }
  956   
  957       public Rectangle getViewRect() {
  958           return viewRect;
  959       }
  960   
  961       public RectSize getIconSize() {
  962           return iconSize;
  963       }
  964   
  965       public RectSize getTextSize() {
  966           return textSize;
  967       }
  968   
  969       public RectSize getAccSize() {
  970           return accSize;
  971       }
  972   
  973       public RectSize getCheckSize() {
  974           return checkSize;
  975       }
  976   
  977       public RectSize getArrowSize() {
  978           return arrowSize;
  979       }
  980   
  981       public RectSize getLabelSize() {
  982           return labelSize;
  983       }
  984   
  985       protected void setMenuItem(JMenuItem mi) {
  986           this.mi = mi;
  987       }
  988   
  989       protected void setMenuItemParent(JComponent miParent) {
  990           this.miParent = miParent;
  991       }
  992   
  993       protected void setFont(Font font) {
  994           this.font = font;
  995       }
  996   
  997       protected void setAccFont(Font accFont) {
  998           this.accFont = accFont;
  999       }
 1000   
 1001       protected void setFontMetrics(FontMetrics fm) {
 1002           this.fm = fm;
 1003       }
 1004   
 1005       protected void setAccFontMetrics(FontMetrics accFm) {
 1006           this.accFm = accFm;
 1007       }
 1008   
 1009       protected void setIcon(Icon icon) {
 1010           this.icon = icon;
 1011       }
 1012   
 1013       protected void setCheckIcon(Icon checkIcon) {
 1014           this.checkIcon = checkIcon;
 1015       }
 1016   
 1017       protected void setArrowIcon(Icon arrowIcon) {
 1018           this.arrowIcon = arrowIcon;
 1019       }
 1020   
 1021       protected void setText(String text) {
 1022           this.text = text;
 1023       }
 1024   
 1025       protected void setAccText(String accText) {
 1026           this.accText = accText;
 1027       }
 1028   
 1029       protected void setColumnLayout(boolean columnLayout) {
 1030           isColumnLayout = columnLayout;
 1031       }
 1032   
 1033       protected void setUseCheckAndArrow(boolean useCheckAndArrow) {
 1034           this.useCheckAndArrow = useCheckAndArrow;
 1035       }
 1036   
 1037       protected void setLeftToRight(boolean leftToRight) {
 1038           isLeftToRight = leftToRight;
 1039       }
 1040   
 1041       protected void setTopLevelMenu(boolean topLevelMenu) {
 1042           isTopLevelMenu = topLevelMenu;
 1043       }
 1044   
 1045       protected void setHtmlView(View htmlView) {
 1046           this.htmlView = htmlView;
 1047       }
 1048   
 1049       protected void setVerticalAlignment(int verticalAlignment) {
 1050           this.verticalAlignment = verticalAlignment;
 1051       }
 1052   
 1053       protected void setHorizontalAlignment(int horizontalAlignment) {
 1054           this.horizontalAlignment = horizontalAlignment;
 1055       }
 1056   
 1057       protected void setVerticalTextPosition(int verticalTextPosition) {
 1058           this.verticalTextPosition = verticalTextPosition;
 1059       }
 1060   
 1061       protected void setHorizontalTextPosition(int horizontalTextPosition) {
 1062           this.horizontalTextPosition = horizontalTextPosition;
 1063       }
 1064   
 1065       protected void setGap(int gap) {
 1066           this.gap = gap;
 1067       }
 1068   
 1069       protected void setLeadingGap(int leadingGap) {
 1070           this.leadingGap = leadingGap;
 1071       }
 1072   
 1073       protected void setAfterCheckIconGap(int afterCheckIconGap) {
 1074           this.afterCheckIconGap = afterCheckIconGap;
 1075       }
 1076   
 1077       protected void setMinTextOffset(int minTextOffset) {
 1078           this.minTextOffset = minTextOffset;
 1079       }
 1080   
 1081       protected void setViewRect(Rectangle viewRect) {
 1082           this.viewRect = viewRect;
 1083       }
 1084   
 1085       protected void setIconSize(RectSize iconSize) {
 1086           this.iconSize = iconSize;
 1087       }
 1088   
 1089       protected void setTextSize(RectSize textSize) {
 1090           this.textSize = textSize;
 1091       }
 1092   
 1093       protected void setAccSize(RectSize accSize) {
 1094           this.accSize = accSize;
 1095       }
 1096   
 1097       protected void setCheckSize(RectSize checkSize) {
 1098           this.checkSize = checkSize;
 1099       }
 1100   
 1101       protected void setArrowSize(RectSize arrowSize) {
 1102           this.arrowSize = arrowSize;
 1103       }
 1104   
 1105       protected void setLabelSize(RectSize labelSize) {
 1106           this.labelSize = labelSize;
 1107       }
 1108   
 1109       public int getLeftTextExtraWidth() {
 1110           return leftTextExtraWidth;
 1111       }
 1112   
 1113       /**
 1114        * Returns false if the component is a JMenu and it is a top
 1115        * level menu (on the menubar).
 1116        */
 1117       public static boolean useCheckAndArrow(JMenuItem menuItem) {
 1118           boolean b = true;
 1119           if ((menuItem instanceof JMenu) &&
 1120                   (((JMenu) menuItem).isTopLevelMenu())) {
 1121               b = false;
 1122           }
 1123           return b;
 1124       }
 1125   
 1126       public static class LayoutResult {
 1127           private Rectangle iconRect;
 1128           private Rectangle textRect;
 1129           private Rectangle accRect;
 1130           private Rectangle checkRect;
 1131           private Rectangle arrowRect;
 1132           private Rectangle labelRect;
 1133   
 1134           public LayoutResult() {
 1135               iconRect = new Rectangle();
 1136               textRect = new Rectangle();
 1137               accRect = new Rectangle();
 1138               checkRect = new Rectangle();
 1139               arrowRect = new Rectangle();
 1140               labelRect = new Rectangle();
 1141           }
 1142   
 1143           public LayoutResult(Rectangle iconRect, Rectangle textRect,
 1144                               Rectangle accRect, Rectangle checkRect,
 1145                               Rectangle arrowRect, Rectangle labelRect) {
 1146               this.iconRect = iconRect;
 1147               this.textRect = textRect;
 1148               this.accRect = accRect;
 1149               this.checkRect = checkRect;
 1150               this.arrowRect = arrowRect;
 1151               this.labelRect = labelRect;
 1152           }
 1153   
 1154           public Rectangle getIconRect() {
 1155               return iconRect;
 1156           }
 1157   
 1158           public void setIconRect(Rectangle iconRect) {
 1159               this.iconRect = iconRect;
 1160           }
 1161   
 1162           public Rectangle getTextRect() {
 1163               return textRect;
 1164           }
 1165   
 1166           public void setTextRect(Rectangle textRect) {
 1167               this.textRect = textRect;
 1168           }
 1169   
 1170           public Rectangle getAccRect() {
 1171               return accRect;
 1172           }
 1173   
 1174           public void setAccRect(Rectangle accRect) {
 1175               this.accRect = accRect;
 1176           }
 1177   
 1178           public Rectangle getCheckRect() {
 1179               return checkRect;
 1180           }
 1181   
 1182           public void setCheckRect(Rectangle checkRect) {
 1183               this.checkRect = checkRect;
 1184           }
 1185   
 1186           public Rectangle getArrowRect() {
 1187               return arrowRect;
 1188           }
 1189   
 1190           public void setArrowRect(Rectangle arrowRect) {
 1191               this.arrowRect = arrowRect;
 1192           }
 1193   
 1194           public Rectangle getLabelRect() {
 1195               return labelRect;
 1196           }
 1197   
 1198           public void setLabelRect(Rectangle labelRect) {
 1199               this.labelRect = labelRect;
 1200           }
 1201   
 1202           public Map<String, Rectangle> getAllRects() {
 1203               Map<String, Rectangle> result = new HashMap<String, Rectangle>();
 1204               result.put("checkRect", checkRect);
 1205               result.put("iconRect", iconRect);
 1206               result.put("textRect", textRect);
 1207               result.put("accRect", accRect);
 1208               result.put("arrowRect", arrowRect);
 1209               result.put("labelRect", labelRect);
 1210               return result;
 1211           }
 1212       }
 1213   
 1214       public static class ColumnAlignment {
 1215           private int checkAlignment;
 1216           private int iconAlignment;
 1217           private int textAlignment;
 1218           private int accAlignment;
 1219           private int arrowAlignment;
 1220   
 1221           public static final ColumnAlignment LEFT_ALIGNMENT =
 1222                   new ColumnAlignment(
 1223                           SwingConstants.LEFT,
 1224                           SwingConstants.LEFT,
 1225                           SwingConstants.LEFT,
 1226                           SwingConstants.LEFT,
 1227                           SwingConstants.LEFT
 1228                   );
 1229   
 1230           public static final ColumnAlignment RIGHT_ALIGNMENT =
 1231                   new ColumnAlignment(
 1232                           SwingConstants.RIGHT,
 1233                           SwingConstants.RIGHT,
 1234                           SwingConstants.RIGHT,
 1235                           SwingConstants.RIGHT,
 1236                           SwingConstants.RIGHT
 1237                   );
 1238   
 1239           public ColumnAlignment(int checkAlignment, int iconAlignment,
 1240                                  int textAlignment, int accAlignment,
 1241                                  int arrowAlignment) {
 1242               this.checkAlignment = checkAlignment;
 1243               this.iconAlignment = iconAlignment;
 1244               this.textAlignment = textAlignment;
 1245               this.accAlignment = accAlignment;
 1246               this.arrowAlignment = arrowAlignment;
 1247           }
 1248   
 1249           public int getCheckAlignment() {
 1250               return checkAlignment;
 1251           }
 1252   
 1253           public int getIconAlignment() {
 1254               return iconAlignment;
 1255           }
 1256   
 1257           public int getTextAlignment() {
 1258               return textAlignment;
 1259           }
 1260   
 1261           public int getAccAlignment() {
 1262               return accAlignment;
 1263           }
 1264   
 1265           public int getArrowAlignment() {
 1266               return arrowAlignment;
 1267           }
 1268       }
 1269   
 1270       public static class RectSize {
 1271           private int width;
 1272           private int height;
 1273           private int origWidth;
 1274           private int maxWidth;
 1275   
 1276           public RectSize() {
 1277           }
 1278   
 1279           public RectSize(int width, int height, int origWidth, int maxWidth) {
 1280               this.width = width;
 1281               this.height = height;
 1282               this.origWidth = origWidth;
 1283               this.maxWidth = maxWidth;
 1284           }
 1285   
 1286           public int getWidth() {
 1287               return width;
 1288           }
 1289   
 1290           public int getHeight() {
 1291               return height;
 1292           }
 1293   
 1294           public int getOrigWidth() {
 1295               return origWidth;
 1296           }
 1297   
 1298           public int getMaxWidth() {
 1299               return maxWidth;
 1300           }
 1301   
 1302           public void setWidth(int width) {
 1303               this.width = width;
 1304           }
 1305   
 1306           public void setHeight(int height) {
 1307               this.height = height;
 1308           }
 1309   
 1310           public void setOrigWidth(int origWidth) {
 1311               this.origWidth = origWidth;
 1312           }
 1313   
 1314           public void setMaxWidth(int maxWidth) {
 1315               this.maxWidth = maxWidth;
 1316           }
 1317   
 1318           public String toString() {
 1319               return "[w=" + width + ",h=" + height + ",ow="
 1320                       + origWidth + ",mw=" + maxWidth + "]";
 1321           }
 1322       }
 1323   }

Home » openjdk-7 » sun » swing » [javadoc | source]