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 javax.swing.plaf.basic;
26
27import java.beans.*;
28import java.awt.*;
29import java.awt.event.KeyEvent;
30import java.awt.event.InputEvent;
31import javax.swing.*;
32import javax.swing.event.DocumentEvent;
33import javax.swing.text.*;
34import javax.swing.plaf.*;
35
36/**
37 * Provides the look and feel for a plain text editor.  In this
38 * implementation the default UI is extended to act as a simple
39 * view factory.
40 * <p>
41 * <strong>Warning:</strong>
42 * Serialized objects of this class will not be compatible with
43 * future Swing releases. The current serialization support is
44 * appropriate for short term storage or RMI between applications running
45 * the same version of Swing.  As of 1.4, support for long term storage
46 * of all JavaBeans&trade;
47 * has been added to the <code>java.beans</code> package.
48 * Please see {@link java.beans.XMLEncoder}.
49 *
50 * @author  Timothy Prinzing
51 */
52@SuppressWarnings("serial") // Same-version serialization only
53public class BasicTextAreaUI extends BasicTextUI {
54
55    /**
56     * Creates a UI for a JTextArea.
57     *
58     * @param ta a text area
59     * @return the UI
60     */
61    public static ComponentUI createUI(JComponent ta) {
62        return new BasicTextAreaUI();
63    }
64
65    /**
66     * Constructs a new BasicTextAreaUI object.
67     */
68    public BasicTextAreaUI() {
69        super();
70    }
71
72    /**
73     * Fetches the name used as a key to look up properties through the
74     * UIManager.  This is used as a prefix to all the standard
75     * text properties.
76     *
77     * @return the name ("TextArea")
78     */
79    protected String getPropertyPrefix() {
80        return "TextArea";
81    }
82
83    protected void installDefaults() {
84        super.installDefaults();
85        //the fix for 4785160 is undone
86    }
87
88    /**
89     * This method gets called when a bound property is changed
90     * on the associated JTextComponent.  This is a hook
91     * which UI implementations may change to reflect how the
92     * UI displays bound properties of JTextComponent subclasses.
93     * This is implemented to rebuild the View when the
94     * <em>WrapLine</em> or the <em>WrapStyleWord</em> property changes.
95     *
96     * @param evt the property change event
97     */
98    protected void propertyChange(PropertyChangeEvent evt) {
99        super.propertyChange(evt);
100        if (evt.getPropertyName().equals("lineWrap") ||
101            evt.getPropertyName().equals("wrapStyleWord") ||
102                evt.getPropertyName().equals("tabSize")) {
103            // rebuild the view
104            modelChanged();
105        } else if ("editable".equals(evt.getPropertyName())) {
106            updateFocusTraversalKeys();
107        }
108    }
109
110
111    /**
112     * The method is overridden to take into account caret width.
113     *
114     * @param c the editor component
115     * @return the preferred size
116     * @throws IllegalArgumentException if invalid value is passed
117     *
118     * @since 1.5
119     */
120    public Dimension getPreferredSize(JComponent c) {
121        return super.getPreferredSize(c);
122        //the fix for 4785160 is undone
123    }
124
125    /**
126     * The method is overridden to take into account caret width.
127     *
128     * @param c the editor component
129     * @return the minimum size
130     * @throws IllegalArgumentException if invalid value is passed
131     *
132     * @since 1.5
133     */
134    public Dimension getMinimumSize(JComponent c) {
135        return super.getMinimumSize(c);
136        //the fix for 4785160 is undone
137    }
138
139    /**
140     * Creates the view for an element.  Returns a WrappedPlainView or
141     * PlainView.
142     *
143     * @param elem the element
144     * @return the view
145     */
146    public View create(Element elem) {
147        Document doc = elem.getDocument();
148        Object i18nFlag = doc.getProperty("i18n"/*AbstractDocument.I18NProperty*/);
149        if ((i18nFlag != null) && i18nFlag.equals(Boolean.TRUE)) {
150            // build a view that support bidi
151            return createI18N(elem);
152        } else {
153            JTextComponent c = getComponent();
154            if (c instanceof JTextArea) {
155                JTextArea area = (JTextArea) c;
156                View v;
157                if (area.getLineWrap()) {
158                    v = new WrappedPlainView(elem, area.getWrapStyleWord());
159                } else {
160                    v = new PlainView(elem);
161                }
162                return v;
163            }
164        }
165        return null;
166    }
167
168    View createI18N(Element elem) {
169        String kind = elem.getName();
170        if (kind != null) {
171            if (kind.equals(AbstractDocument.ContentElementName)) {
172                return new PlainParagraph(elem);
173            } else if (kind.equals(AbstractDocument.ParagraphElementName)) {
174                return new BoxView(elem, View.Y_AXIS);
175            }
176        }
177        return null;
178    }
179
180    /**
181     * Returns the baseline.
182     *
183     * @throws NullPointerException {@inheritDoc}
184     * @throws IllegalArgumentException {@inheritDoc}
185     * @see javax.swing.JComponent#getBaseline(int, int)
186     * @since 1.6
187     */
188    public int getBaseline(JComponent c, int width, int height) {
189        super.getBaseline(c, width, height);
190        Object i18nFlag = ((JTextComponent)c).getDocument().
191                                              getProperty("i18n");
192        Insets insets = c.getInsets();
193        if (Boolean.TRUE.equals(i18nFlag)) {
194            View rootView = getRootView((JTextComponent)c);
195            if (rootView.getViewCount() > 0) {
196                height = height - insets.top - insets.bottom;
197                int baseline = insets.top;
198                int fieldBaseline = BasicHTML.getBaseline(
199                        rootView.getView(0), width - insets.left -
200                        insets.right, height);
201                if (fieldBaseline < 0) {
202                    return -1;
203                }
204                return baseline + fieldBaseline;
205            }
206            return -1;
207        }
208        FontMetrics fm = c.getFontMetrics(c.getFont());
209        return insets.top + fm.getAscent();
210    }
211
212    /**
213     * Returns an enum indicating how the baseline of the component
214     * changes as the size changes.
215     *
216     * @throws NullPointerException {@inheritDoc}
217     * @see javax.swing.JComponent#getBaseline(int, int)
218     * @since 1.6
219     */
220    public Component.BaselineResizeBehavior getBaselineResizeBehavior(
221            JComponent c) {
222        super.getBaselineResizeBehavior(c);
223        return Component.BaselineResizeBehavior.CONSTANT_ASCENT;
224    }
225
226
227    /**
228     * Paragraph for representing plain-text lines that support
229     * bidirectional text.
230     */
231    static class PlainParagraph extends ParagraphView {
232
233        PlainParagraph(Element elem) {
234            super(elem);
235            layoutPool = new LogicalView(elem);
236            layoutPool.setParent(this);
237        }
238
239        public void setParent(View parent) {
240            super.setParent(parent);
241            if (parent != null) {
242                setPropertiesFromAttributes();
243            }
244        }
245
246        protected void setPropertiesFromAttributes() {
247            Component c = getContainer();
248            if ((c != null) && (! c.getComponentOrientation().isLeftToRight())) {
249                setJustification(StyleConstants.ALIGN_RIGHT);
250            } else {
251                setJustification(StyleConstants.ALIGN_LEFT);
252            }
253        }
254
255        /**
256         * Fetch the constraining span to flow against for
257         * the given child index.
258         */
259        public int getFlowSpan(int index) {
260            Component c = getContainer();
261            if (c instanceof JTextArea) {
262                JTextArea area = (JTextArea) c;
263                if (! area.getLineWrap()) {
264                    // no limit if unwrapped
265                    return Integer.MAX_VALUE;
266                }
267            }
268            return super.getFlowSpan(index);
269        }
270
271        protected SizeRequirements calculateMinorAxisRequirements(int axis,
272                                                                  SizeRequirements r) {
273            SizeRequirements req = super.calculateMinorAxisRequirements(axis, r);
274            Component c = getContainer();
275            if (c instanceof JTextArea) {
276                JTextArea area = (JTextArea) c;
277                if (! area.getLineWrap()) {
278                    // min is pref if unwrapped
279                    req.minimum = req.preferred;
280                } else {
281                    req.minimum = 0;
282                    req.preferred = getWidth();
283                    if (req.preferred == Integer.MAX_VALUE) {
284                        // We have been initially set to MAX_VALUE, but we
285                        // don't want this as our preferred.
286                        req.preferred = 100;
287                    }
288                }
289            }
290            return req;
291        }
292
293        /**
294         * Sets the size of the view.  If the size has changed, layout
295         * is redone.  The size is the full size of the view including
296         * the inset areas.
297         *
298         * @param width the width >= 0
299         * @param height the height >= 0
300         */
301        public void setSize(float width, float height) {
302            if ((int) width != getWidth()) {
303                preferenceChanged(null, true, true);
304            }
305            super.setSize(width, height);
306        }
307
308        /**
309         * This class can be used to represent a logical view for
310         * a flow.  It keeps the children updated to reflect the state
311         * of the model, gives the logical child views access to the
312         * view hierarchy, and calculates a preferred span.  It doesn't
313         * do any rendering, layout, or model/view translation.
314         */
315        static class LogicalView extends CompositeView {
316
317            LogicalView(Element elem) {
318                super(elem);
319            }
320
321            protected int getViewIndexAtPosition(int pos) {
322                Element elem = getElement();
323                if (elem.getElementCount() > 0) {
324                    return elem.getElementIndex(pos);
325                }
326                return 0;
327            }
328
329            protected boolean updateChildren(DocumentEvent.ElementChange ec,
330                                             DocumentEvent e, ViewFactory f) {
331                return false;
332            }
333
334            protected void loadChildren(ViewFactory f) {
335                Element elem = getElement();
336                if (elem.getElementCount() > 0) {
337                    super.loadChildren(f);
338                } else {
339                    View v = new GlyphView(elem);
340                    append(v);
341                }
342            }
343
344            public float getPreferredSpan(int axis) {
345                if( getViewCount() != 1 )
346                    throw new Error("One child view is assumed.");
347
348                View v = getView(0);
349                return v.getPreferredSpan(axis);
350            }
351
352            /**
353             * Forward the DocumentEvent to the given child view.  This
354             * is implemented to reparent the child to the logical view
355             * (the children may have been parented by a row in the flow
356             * if they fit without breaking) and then execute the superclass
357             * behavior.
358             *
359             * @param v the child view to forward the event to.
360             * @param e the change information from the associated document
361             * @param a the current allocation of the view
362             * @param f the factory to use to rebuild if the view has children
363             * @see #forwardUpdate
364             * @since 1.3
365             */
366            protected void forwardUpdateToView(View v, DocumentEvent e,
367                                               Shape a, ViewFactory f) {
368                v.setParent(this);
369                super.forwardUpdateToView(v, e, a, f);
370            }
371
372            // The following methods don't do anything useful, they
373            // simply keep the class from being abstract.
374
375            public void paint(Graphics g, Shape allocation) {
376            }
377
378            protected boolean isBefore(int x, int y, Rectangle alloc) {
379                return false;
380            }
381
382            protected boolean isAfter(int x, int y, Rectangle alloc) {
383                return false;
384            }
385
386            protected View getViewAtPoint(int x, int y, Rectangle alloc) {
387                return null;
388            }
389
390            protected void childAllocation(int index, Rectangle a) {
391            }
392        }
393    }
394
395}
396