1 /*
   2  * Copyright (c) 1997, 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 package com.sun.java.swing.plaf.motif;
  26 
  27 import sun.swing.SwingUtilities2;
  28 
  29 import javax.swing.*;
  30 import java.awt.Color;
  31 import java.awt.Dimension;
  32 import java.awt.Graphics;
  33 import java.awt.Font;
  34 import java.awt.FontMetrics;
  35 import java.awt.Rectangle;
  36 import java.awt.Component;
  37 import java.awt.Insets;
  38 import java.awt.event.KeyEvent;
  39 import java.awt.Container;
  40 
  41 import javax.swing.plaf.basic.*;
  42 import javax.swing.text.View;
  43 
  44 /*
  45  * @author Jeff Dinkins
  46  * @author Dave Kloba
  47  */
  48 
  49 public class MotifGraphicsUtils implements SwingConstants
  50 {
  51     /* Client Property keys for text and accelerator text widths */
  52     private static final String MAX_ACC_WIDTH  =  "maxAccWidth";
  53 
  54     /**
  55      * Draws the point (<b>x</b>, <b>y</b>) in the current color.
  56      */
  57     static void drawPoint(Graphics g, int x, int y) {
  58         g.drawLine(x, y, x, y);
  59     }
  60 
  61     /*
  62      * Convenience method for drawing a grooved line
  63      *
  64      */
  65     public static void drawGroove(Graphics g, int x, int y, int w, int h,
  66                                   Color shadow, Color highlight)
  67     {
  68         Color oldColor = g.getColor();  // Make no net change to g
  69         g.translate(x, y);
  70 
  71         g.setColor(shadow);
  72         g.drawRect(0, 0, w-2, h-2);
  73 
  74         g.setColor(highlight);
  75         g.drawLine(1, h-3, 1, 1);
  76         g.drawLine(1, 1, w-3, 1);
  77 
  78         g.drawLine(0, h-1, w-1, h-1);
  79         g.drawLine(w-1, h-1, w-1, 0);
  80 
  81         g.translate(-x, -y);
  82         g.setColor(oldColor);
  83     }
  84 
  85     /** Draws <b>aString</b> in the rectangle defined by
  86       * (<b>x</b>, <b>y</b>, <b>width</b>, <b>height</b>).
  87       * <b>justification</b> specifies the text's justification, one of
  88       * LEFT, CENTER, or RIGHT.
  89       * <b>drawStringInRect()</b> does not clip to the rectangle, but instead
  90       * uses this rectangle and the desired justification to compute the point
  91       * at which to begin drawing the text.
  92       * @see #drawString
  93       */
  94     public static void drawStringInRect(Graphics g, String aString, int x, int y,
  95                                  int width, int height, int justification) {
  96         drawStringInRect(null, g, aString, x, y, width, height, justification);
  97     }
  98 
  99     static void drawStringInRect(JComponent c, Graphics g, String aString,
 100                                  int x, int y, int width, int height,
 101                                  int justification) {
 102         FontMetrics  fontMetrics;
 103         int          drawWidth, startX, startY, delta;
 104 
 105         if (g.getFont() == null) {
 106 //            throw new InconsistencyException("No font set");
 107             return;
 108         }
 109         fontMetrics = SwingUtilities2.getFontMetrics(c, g);
 110         if (fontMetrics == null) {
 111 //            throw new InconsistencyException("No metrics for Font " + font());
 112             return;
 113         }
 114 
 115         if (justification == CENTER) {
 116             drawWidth = SwingUtilities2.getTextUIDrawing(c)
 117                     .getStringWidth(c, fontMetrics, aString);
 118             if (drawWidth > width) {
 119                 drawWidth = width;
 120             }
 121             startX = x + (width - drawWidth) / 2;
 122         } else if (justification == RIGHT) {
 123             drawWidth = SwingUtilities2.getTextUIDrawing(c)
 124                     .getStringWidth(c, fontMetrics, aString);
 125             if (drawWidth > width) {
 126                 drawWidth = width;
 127             }
 128             startX = x + width - drawWidth;
 129         } else {
 130             startX = x;
 131         }
 132 
 133         delta = (height - fontMetrics.getAscent() - fontMetrics.getDescent()) / 2;
 134         if (delta < 0) {
 135             delta = 0;
 136         }
 137 
 138         startY = y + height - delta - fontMetrics.getDescent();
 139 
 140         SwingUtilities2.getTextUIDrawing(c)
 141                 .drawString(c, g, aString, startX, startY);
 142     }
 143 
 144   /**
 145    * This method is not being used to paint menu item since
 146    * 6.0 This code left for compatibility only. Do not use or
 147    * override it, this will not cause any visible effect.
 148    */
 149   public static void paintMenuItem(Graphics g, JComponent c,
 150                                    Icon checkIcon, Icon arrowIcon,
 151                                    Color background, Color foreground,
 152                                    int defaultTextIconGap)
 153     {
 154 
 155         JMenuItem b = (JMenuItem) c;
 156         ButtonModel model = b.getModel();
 157 
 158         Dimension size = b.getSize();
 159         Insets i = c.getInsets();
 160 
 161         Rectangle viewRect = new Rectangle(size);
 162 
 163         viewRect.x += i.left;
 164         viewRect.y += i.top;
 165         viewRect.width -= (i.right + viewRect.x);
 166         viewRect.height -= (i.bottom + viewRect.y);
 167 
 168         Rectangle iconRect = new Rectangle();
 169         Rectangle textRect = new Rectangle();
 170         Rectangle acceleratorRect = new Rectangle();
 171         Rectangle checkRect = new Rectangle();
 172         Rectangle arrowRect = new Rectangle();
 173 
 174         Font holdf = g.getFont();
 175         Font f = c.getFont();
 176         g.setFont(f);
 177         FontMetrics fm = SwingUtilities2.getFontMetrics(c, g, f);
 178         FontMetrics fmAccel = SwingUtilities2.getFontMetrics(
 179             c, g, UIManager.getFont("MenuItem.acceleratorFont"));
 180 
 181         if (c.isOpaque()) {
 182             if (model.isArmed()|| (c instanceof JMenu && model.isSelected())) {
 183                 g.setColor(background);
 184             } else {
 185                 g.setColor(c.getBackground());
 186             }
 187             g.fillRect(0,0, size.width, size.height);
 188         }
 189 
 190         // get Accelerator text
 191         KeyStroke accelerator =  b.getAccelerator();
 192         String acceleratorText = "";
 193         if (accelerator != null) {
 194             int modifiers = accelerator.getModifiers();
 195             if (modifiers > 0) {
 196                 acceleratorText = KeyEvent.getKeyModifiersText(modifiers);
 197                 acceleratorText += "+";
 198             }
 199             acceleratorText += KeyEvent.getKeyText(accelerator.getKeyCode());
 200         }
 201 
 202         // layout the text and icon
 203         String text = layoutMenuItem(c, fm, b.getText(), fmAccel,
 204                                      acceleratorText, b.getIcon(),
 205                                      checkIcon, arrowIcon,
 206                                      b.getVerticalAlignment(),
 207                                      b.getHorizontalAlignment(),
 208                                      b.getVerticalTextPosition(),
 209                                      b.getHorizontalTextPosition(),
 210                                      viewRect, iconRect,
 211                                      textRect, acceleratorRect,
 212                                      checkRect, arrowRect,
 213                                      b.getText() == null
 214                                      ? 0 : defaultTextIconGap,
 215                                      defaultTextIconGap
 216                                      );
 217 
 218         // Paint the Check
 219         Color holdc = g.getColor();
 220         if (checkIcon != null) {
 221             if(model.isArmed() || (c instanceof JMenu && model.isSelected()))
 222                 g.setColor(foreground);
 223             checkIcon.paintIcon(c, g, checkRect.x, checkRect.y);
 224             g.setColor(holdc);
 225         }
 226 
 227         // Paint the Icon
 228         if(b.getIcon() != null) {
 229             Icon icon;
 230             if(!model.isEnabled()) {
 231                 icon = b.getDisabledIcon();
 232             } else if(model.isPressed() && model.isArmed()) {
 233                 icon = b.getPressedIcon();
 234                 if(icon == null) {
 235                     // Use default icon
 236                     icon = b.getIcon();
 237                 }
 238             } else {
 239                 icon = b.getIcon();
 240             }
 241 
 242             if (icon!=null) {
 243                 icon.paintIcon(c, g, iconRect.x, iconRect.y);
 244             }
 245         }
 246 
 247         // Draw the Text
 248         if(text != null && !text.equals("")) {
 249             // Once BasicHTML becomes public, use BasicHTML.propertyKey
 250             // instead of the hardcoded string below!
 251             View v = (View) c.getClientProperty("html");
 252             if (v != null) {
 253                 v.paint(g, textRect);
 254             } else {
 255                 int mnemIndex = b.getDisplayedMnemonicIndex();
 256 
 257                 if(!model.isEnabled()) {
 258                     // *** paint the text disabled
 259                     g.setColor(b.getBackground().brighter());
 260                     SwingUtilities2.getTextUIDrawing(c).drawStringUnderlineCharAt(b, g,text,
 261                         mnemIndex,
 262                         textRect.x, textRect.y + fmAccel.getAscent());
 263                     g.setColor(b.getBackground().darker());
 264                     SwingUtilities2.getTextUIDrawing(c).
 265                             drawStringUnderlineCharAt(b, g,text,
 266                         mnemIndex,
 267                         textRect.x - 1, textRect.y + fmAccel.getAscent() - 1);
 268 
 269                 } else {
 270                     // *** paint the text normally
 271                     if (model.isArmed()|| (c instanceof JMenu && model.isSelected())) {
 272                         g.setColor(foreground);
 273                     } else {
 274                         g.setColor(b.getForeground());
 275                     }
 276                     SwingUtilities2.getTextUIDrawing(c)
 277                             .drawStringUnderlineCharAt(b, g,text,
 278                                                   mnemIndex,
 279                                                   textRect.x,
 280                                                   textRect.y + fm.getAscent());
 281                 }
 282             }
 283         }
 284 
 285         // Draw the Accelerator Text
 286         if(acceleratorText != null && !acceleratorText.equals("")) {
 287 
 288             //Get the maxAccWidth from the parent to calculate the offset.
 289             int accOffset = 0;
 290             Container parent = b.getParent();
 291             if (parent != null && parent instanceof JComponent) {
 292                 JComponent p = (JComponent) parent;
 293                 Integer maxValueInt = (Integer) p.getClientProperty(MotifGraphicsUtils.MAX_ACC_WIDTH);
 294                 int maxValue = maxValueInt != null ?
 295                     maxValueInt.intValue() : acceleratorRect.width;
 296 
 297                 //Calculate the offset, with which the accelerator texts will be drawn with.
 298                 accOffset = maxValue - acceleratorRect.width;
 299             }
 300 
 301             g.setFont( UIManager.getFont("MenuItem.acceleratorFont") );
 302             if(!model.isEnabled()) {
 303                 // *** paint the acceleratorText disabled
 304                 g.setColor(b.getBackground().brighter());
 305                 SwingUtilities2.getTextUIDrawing(c).drawString(c, g,acceleratorText,
 306                                               acceleratorRect.x - accOffset, acceleratorRect.y + fm.getAscent());
 307                 g.setColor(b.getBackground().darker());
 308                 SwingUtilities2.getTextUIDrawing(c).drawString(c, g,acceleratorText,
 309                                               acceleratorRect.x - accOffset - 1, acceleratorRect.y + fm.getAscent() - 1);
 310             } else {
 311                 // *** paint the acceleratorText normally
 312                 if (model.isArmed()|| (c instanceof JMenu && model.isSelected()))
 313                     {
 314                         g.setColor(foreground);
 315                     } else {
 316                         g.setColor(b.getForeground());
 317                     }
 318                 SwingUtilities2.getTextUIDrawing(c).drawString(c, g,acceleratorText,
 319                                               acceleratorRect.x - accOffset,
 320                                               acceleratorRect.y + fmAccel.getAscent());
 321             }
 322         }
 323 
 324         // Paint the Arrow
 325         if (arrowIcon != null) {
 326             if(model.isArmed() || (c instanceof JMenu && model.isSelected()))
 327                 g.setColor(foreground);
 328             if( !(b.getParent() instanceof JMenuBar) )
 329                 arrowIcon.paintIcon(c, g, arrowRect.x, arrowRect.y);
 330         }
 331 
 332         g.setColor(holdc);
 333         g.setFont(holdf);
 334     }
 335 
 336 
 337     /**
 338      * Compute and return the location of the icons origin, the
 339      * location of origin of the text baseline, and a possibly clipped
 340      * version of the compound labels string.  Locations are computed
 341      * relative to the viewR rectangle.
 342      */
 343 
 344     private static String layoutMenuItem(
 345         JComponent c,
 346         FontMetrics fm,
 347         String text,
 348         FontMetrics fmAccel,
 349         String acceleratorText,
 350         Icon icon,
 351         Icon checkIcon,
 352         Icon arrowIcon,
 353         int verticalAlignment,
 354         int horizontalAlignment,
 355         int verticalTextPosition,
 356         int horizontalTextPosition,
 357         Rectangle viewR,
 358         Rectangle iconR,
 359         Rectangle textR,
 360         Rectangle acceleratorR,
 361         Rectangle checkIconR,
 362         Rectangle arrowIconR,
 363         int textIconGap,
 364         int menuItemGap
 365         )
 366     {
 367 
 368         SwingUtilities.layoutCompoundLabel(c,
 369                                            fm,
 370                                            text,
 371                                            icon,
 372                                            verticalAlignment,
 373                                            horizontalAlignment,
 374                                            verticalTextPosition,
 375                                            horizontalTextPosition,
 376                                            viewR,
 377                                            iconR,
 378                                            textR,
 379                                            textIconGap);
 380 
 381         /* Initialize the acceelratorText bounds rectangle textR.  If a null
 382          * or and empty String was specified we substitute "" here
 383          * and use 0,0,0,0 for acceleratorTextR.
 384          */
 385         if( (acceleratorText == null) || acceleratorText.equals("") ) {
 386             acceleratorR.width = acceleratorR.height = 0;
 387             acceleratorText = "";
 388         }
 389         else {
 390             acceleratorR.width
 391                 = SwingUtilities2.getTextUIDrawing(c)
 392                         .getStringWidth(c, fmAccel, acceleratorText);
 393             acceleratorR.height = fmAccel.getHeight();
 394         }
 395 
 396         /* Initialize the checkIcon bounds rectangle checkIconR.
 397          */
 398 
 399         if (checkIcon != null) {
 400             checkIconR.width = checkIcon.getIconWidth();
 401             checkIconR.height = checkIcon.getIconHeight();
 402         }
 403         else {
 404             checkIconR.width = checkIconR.height = 0;
 405         }
 406 
 407         /* Initialize the arrowIcon bounds rectangle arrowIconR.
 408          */
 409 
 410         if (arrowIcon != null) {
 411             arrowIconR.width = arrowIcon.getIconWidth();
 412             arrowIconR.height = arrowIcon.getIconHeight();
 413         }
 414         else {
 415             arrowIconR.width = arrowIconR.height = 0;
 416         }
 417 
 418 
 419         Rectangle labelR = iconR.union(textR);
 420         if( MotifGraphicsUtils.isLeftToRight(c) ) {
 421             textR.x += checkIconR.width + menuItemGap;
 422             iconR.x += checkIconR.width + menuItemGap;
 423 
 424             // Position the Accelerator text rect
 425             acceleratorR.x = viewR.x + viewR.width - arrowIconR.width
 426                              - menuItemGap - acceleratorR.width;
 427 
 428             // Position the Check and Arrow Icons
 429             checkIconR.x = viewR.x;
 430             arrowIconR.x = viewR.x + viewR.width - menuItemGap
 431                            - arrowIconR.width;
 432         } else {
 433             textR.x -= (checkIconR.width + menuItemGap);
 434             iconR.x -= (checkIconR.width + menuItemGap);
 435 
 436             // Position the Accelerator text rect
 437             acceleratorR.x = viewR.x + arrowIconR.width + menuItemGap;
 438 
 439             // Position the Check and Arrow Icons
 440             checkIconR.x = viewR.x + viewR.width - checkIconR.width;
 441             arrowIconR.x = viewR.x + menuItemGap;
 442         }
 443 
 444         // Align the accelertor text and the check and arrow icons vertically
 445         // with the center of the label rect.
 446         acceleratorR.y = labelR.y + (labelR.height/2) - (acceleratorR.height/2);
 447         arrowIconR.y = labelR.y + (labelR.height/2) - (arrowIconR.height/2);
 448         checkIconR.y = labelR.y + (labelR.height/2) - (checkIconR.height/2);
 449 
 450         /*
 451           System.out.println("Layout: v=" +viewR+"  c="+checkIconR+" i="+
 452           iconR+" t="+textR+" acc="+acceleratorR+" a="+arrowIconR);
 453           */
 454         return text;
 455     }
 456 
 457   private static void drawMenuBezel(Graphics g, Color background,
 458                                     int x, int y,
 459                                     int width, int height)
 460     {
 461       // shadowed button region
 462       g.setColor(background);
 463       g.fillRect(x,y,width,height);
 464 
 465       g.setColor(background.brighter().brighter());
 466       g.drawLine(x+1,       y+height-1,  x+width-1, y+height-1);
 467       g.drawLine(x+width-1, y+height-2,  x+width-1, y+1);
 468 
 469       g.setColor(background.darker().darker());
 470       g.drawLine(x,   y,   x+width-2, y);
 471       g.drawLine(x,   y+1, x,         y+height-2);
 472 
 473     }
 474 
 475     /*
 476      * Convenience function for determining ComponentOrientation.  Helps us
 477      * avoid having Munge directives throughout the code.
 478      */
 479     static boolean isLeftToRight( Component c ) {
 480         return c.getComponentOrientation().isLeftToRight();
 481     }
 482 }