1 /*
   2  * Copyright (c) 1997, 2014, 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 java.awt.*;
  28 import javax.swing.*;
  29 import javax.swing.plaf.*;
  30 import javax.swing.border.*;
  31 import javax.swing.plaf.basic.*;
  32 import java.io.Serializable;
  33 import java.awt.event.*;
  34 import java.beans.*;
  35 
  36 /**
  37  * ComboBox motif look and feel
  38  * <p> * <strong>Warning:</strong>
  39  * Serialized objects of this class will not be compatible with
  40  * future Swing releases.  The current serialization support is appropriate
  41  * for short term storage or RMI between applications running the same
  42  * version of Swing.  A future release of Swing will provide support for
  43  * long term persistence.
  44  *
  45  * @author Arnaud Weber
  46  */
  47 @SuppressWarnings("serial") // Same-version serialization only
  48 public class MotifComboBoxUI extends BasicComboBoxUI implements Serializable {
  49     Icon arrowIcon;
  50     static final int HORIZ_MARGIN = 3;
  51 
  52     public static ComponentUI createUI(JComponent c) {
  53         return new MotifComboBoxUI();
  54     }
  55 
  56     public void installUI(JComponent c) {
  57         super.installUI(c);
  58         arrowIcon = new MotifComboBoxArrowIcon(UIManager.getColor("controlHighlight"),
  59                                                UIManager.getColor("controlShadow"),
  60                                                UIManager.getColor("control"));
  61 
  62         Runnable initCode = new Runnable() {
  63             public void run(){
  64                 if ( motifGetEditor() != null ) {
  65                     motifGetEditor().setBackground( UIManager.getColor( "text" ) );
  66                 }
  67             }
  68         };
  69 
  70         SwingUtilities.invokeLater( initCode );
  71     }
  72 
  73     public Dimension getMinimumSize( JComponent c ) {
  74         if ( !isMinimumSizeDirty ) {
  75             return new Dimension( cachedMinimumSize );
  76         }
  77         Dimension size;
  78         Insets insets = getInsets();
  79         size = getDisplaySize();
  80         size.height += insets.top + insets.bottom;
  81         int buttonSize = iconAreaWidth();
  82         size.width +=  insets.left + insets.right + buttonSize;
  83 
  84         cachedMinimumSize.setSize( size.width, size.height );
  85         isMinimumSizeDirty = false;
  86 
  87         return size;
  88     }
  89 
  90     protected ComboPopup createPopup() {
  91         return new MotifComboPopup( comboBox );
  92     }
  93 
  94     /**
  95      * Overriden to empty the MouseMotionListener.
  96      */
  97     @SuppressWarnings("serial") // Superclass is not serializable across versions
  98     protected class MotifComboPopup extends BasicComboPopup {
  99 
 100         public MotifComboPopup( JComboBox<Object> comboBox ) {
 101             super( comboBox );
 102         }
 103 
 104         /**
 105          * Motif combo popup should not track the mouse in the list.
 106          */
 107         public MouseMotionListener createListMouseMotionListener() {
 108            return new MouseMotionAdapter() {};
 109         }
 110 
 111         public KeyListener createKeyListener() {
 112             return super.createKeyListener();
 113         }
 114 
 115         protected class InvocationKeyHandler extends BasicComboPopup.InvocationKeyHandler {
 116             protected InvocationKeyHandler() {
 117                 MotifComboPopup.this.super();
 118             }
 119         }
 120     }
 121 
 122     protected void installComponents() {
 123         if ( comboBox.isEditable() ) {
 124             addEditor();
 125         }
 126 
 127         comboBox.add( currentValuePane );
 128     }
 129 
 130     protected void uninstallComponents() {
 131         removeEditor();
 132         comboBox.removeAll();
 133     }
 134 
 135     public void paint(Graphics g, JComponent c) {
 136         boolean hasFocus = comboBox.hasFocus();
 137         Rectangle r;
 138 
 139         if (comboBox.isEnabled()) {
 140             g.setColor(comboBox.getBackground());
 141         } else {
 142             g.setColor(UIManager.getColor("ComboBox.disabledBackground"));
 143         }
 144         g.fillRect(0,0,c.getWidth(),c.getHeight());
 145 
 146         if ( !comboBox.isEditable() ) {
 147             r = rectangleForCurrentValue();
 148             paintCurrentValue(g,r,hasFocus);
 149         }
 150         r = rectangleForArrowIcon();
 151         arrowIcon.paintIcon(c,g,r.x,r.y);
 152         if ( !comboBox.isEditable() ) {
 153             Border border = comboBox.getBorder();
 154             Insets in;
 155             if ( border != null ) {
 156                 in = border.getBorderInsets(comboBox);
 157             }
 158             else {
 159                 in = new Insets( 0, 0, 0, 0 );
 160             }
 161             // Draw the separation
 162             if(MotifGraphicsUtils.isLeftToRight(comboBox)) {
 163                 r.x -= (HORIZ_MARGIN + 2);
 164             }
 165             else {
 166                 r.x += r.width + HORIZ_MARGIN + 1;
 167             }
 168             r.y = in.top;
 169             r.width = 1;
 170             r.height = comboBox.getBounds().height - in.bottom - in.top;
 171             g.setColor(UIManager.getColor("controlShadow"));
 172             g.fillRect(r.x,r.y,r.width,r.height);
 173             r.x++;
 174             g.setColor(UIManager.getColor("controlHighlight"));
 175             g.fillRect(r.x,r.y,r.width,r.height);
 176         }
 177     }
 178 
 179     public void paintCurrentValue(Graphics g,Rectangle bounds,boolean hasFocus) {
 180         ListCellRenderer<Object> renderer = comboBox.getRenderer();
 181         Component c;
 182         Dimension d;
 183         c = renderer.getListCellRendererComponent(listBox, comboBox.getSelectedItem(), -1, false, false);
 184         c.setFont(comboBox.getFont());
 185         if ( comboBox.isEnabled() ) {
 186             c.setForeground(comboBox.getForeground());
 187             c.setBackground(comboBox.getBackground());
 188         }
 189         else {
 190             c.setForeground(UIManager.getColor("ComboBox.disabledForeground"));
 191             c.setBackground(UIManager.getColor("ComboBox.disabledBackground"));
 192         }
 193         d  = c.getPreferredSize();
 194         currentValuePane.paintComponent(g,c,comboBox,bounds.x,bounds.y,
 195                                         bounds.width,d.height);
 196     }
 197 
 198     protected Rectangle rectangleForArrowIcon() {
 199         Rectangle b = comboBox.getBounds();
 200         Border border = comboBox.getBorder();
 201         Insets in;
 202         if ( border != null ) {
 203             in = border.getBorderInsets(comboBox);
 204         }
 205         else {
 206             in = new Insets( 0, 0, 0, 0 );
 207         }
 208         b.x = in.left;
 209         b.y = in.top;
 210         b.width -= (in.left + in.right);
 211         b.height -= (in.top + in.bottom);
 212 
 213         if(MotifGraphicsUtils.isLeftToRight(comboBox)) {
 214             b.x = b.x + b.width - HORIZ_MARGIN - arrowIcon.getIconWidth();
 215         }
 216         else {
 217             b.x += HORIZ_MARGIN;
 218         }
 219         b.y = b.y + (b.height - arrowIcon.getIconHeight()) / 2;
 220         b.width = arrowIcon.getIconWidth();
 221         b.height = arrowIcon.getIconHeight();
 222         return b;
 223     }
 224 
 225     protected Rectangle rectangleForCurrentValue() {
 226         int width = comboBox.getWidth();
 227         int height = comboBox.getHeight();
 228         Insets insets = getInsets();
 229         if(MotifGraphicsUtils.isLeftToRight(comboBox)) {
 230             return new Rectangle(insets.left, insets.top,
 231                                  (width - (insets.left + insets.right)) -
 232                                                         iconAreaWidth(),
 233                                  height - (insets.top + insets.bottom));
 234         }
 235         else {
 236             return new Rectangle(insets.left + iconAreaWidth(), insets.top,
 237                                  (width - (insets.left + insets.right)) -
 238                                                         iconAreaWidth(),
 239                                  height - (insets.top + insets.bottom));
 240         }
 241     }
 242 
 243     public int iconAreaWidth() {
 244         if ( comboBox.isEditable() )
 245             return arrowIcon.getIconWidth() + (2 * HORIZ_MARGIN);
 246         else
 247             return arrowIcon.getIconWidth() + (3 * HORIZ_MARGIN) + 2;
 248     }
 249 
 250     public void configureEditor() {
 251         super.configureEditor();
 252         editor.setBackground( UIManager.getColor( "text" ) );
 253     }
 254 
 255     protected LayoutManager createLayoutManager() {
 256         return new ComboBoxLayoutManager();
 257     }
 258 
 259     private Component motifGetEditor() {
 260         return editor;
 261     }
 262 
 263     /**
 264      * This inner class is marked &quot;public&quot; due to a compiler bug.
 265      * This class should be treated as a &quot;protected&quot; inner class.
 266      * Instantiate it only within subclasses of <FooUI>.
 267      */
 268     public class ComboBoxLayoutManager extends BasicComboBoxUI.ComboBoxLayoutManager {
 269         public ComboBoxLayoutManager() {
 270             MotifComboBoxUI.this.super();
 271         }
 272         public void layoutContainer(Container parent) {
 273             if ( motifGetEditor() != null ) {
 274                 Rectangle cvb = rectangleForCurrentValue();
 275                 cvb.x += 1;
 276                 cvb.y += 1;
 277                 cvb.width -= 1;
 278                 cvb.height -= 2;
 279                 motifGetEditor().setBounds(cvb);
 280             }
 281         }
 282     }
 283 
 284     @SuppressWarnings("serial") // Same-version serialization only
 285     static class MotifComboBoxArrowIcon implements Icon, Serializable {
 286         private Color lightShadow;
 287         private Color darkShadow;
 288         private Color fill;
 289 
 290         public MotifComboBoxArrowIcon(Color lightShadow, Color darkShadow, Color fill) {
 291             this.lightShadow = lightShadow;
 292             this.darkShadow = darkShadow;
 293             this.fill = fill;
 294         }
 295 
 296 
 297         public void paintIcon(Component c, Graphics g, int xo, int yo) {
 298             int w = getIconWidth();
 299             int h = getIconHeight();
 300 
 301             g.setColor(lightShadow);
 302             g.drawLine(xo, yo, xo+w-1, yo);
 303             g.drawLine(xo, yo+1, xo+w-3, yo+1);
 304             g.setColor(darkShadow);
 305             g.drawLine(xo+w-2, yo+1, xo+w-1, yo+1);
 306 
 307             for ( int x = xo+1, y = yo+2, dx = w-6; y+1 < yo+h; y += 2 ) {
 308                 g.setColor(lightShadow);
 309                 g.drawLine(x, y,   x+1, y);
 310                 g.drawLine(x, y+1, x+1, y+1);
 311                 if ( dx > 0 ) {
 312                     g.setColor(fill);
 313                     g.drawLine(x+2, y,   x+1+dx, y);
 314                     g.drawLine(x+2, y+1, x+1+dx, y+1);
 315                 }
 316                 g.setColor(darkShadow);
 317                 g.drawLine(x+dx+2, y,   x+dx+3, y);
 318                 g.drawLine(x+dx+2, y+1, x+dx+3, y+1);
 319                 x += 1;
 320                 dx -= 2;
 321             }
 322 
 323             g.setColor(darkShadow);
 324             g.drawLine(xo+(w/2), yo+h-1, xo+(w/2), yo+h-1);
 325 
 326         }
 327 
 328         public int getIconWidth() {
 329             return 11;
 330         }
 331 
 332         public int getIconHeight() {
 333             return 11;
 334         }
 335     }
 336 
 337     /**
 338      *{@inheritDoc}
 339      *
 340      * @since 1.6
 341      */
 342     protected PropertyChangeListener createPropertyChangeListener() {
 343         return new MotifPropertyChangeListener();
 344     }
 345 
 346     /**
 347      * This class should be made &quot;protected&quot; in future releases.
 348      */
 349     private class MotifPropertyChangeListener
 350             extends BasicComboBoxUI.PropertyChangeHandler {
 351         public void propertyChange(PropertyChangeEvent e) {
 352             super.propertyChange(e);
 353             String propertyName = e.getPropertyName();
 354             if (propertyName == "enabled") {
 355                 if (comboBox.isEnabled()) {
 356                     Component editor = motifGetEditor();
 357                     if (editor != null) {
 358                         editor.setBackground(UIManager.getColor("text"));
 359                     }
 360                 }
 361             }
 362         }
 363     }
 364 }