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