1. /*
  2. * @(#)BasicTextFieldUI.java 1.92 03/03/17
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.plaf.basic;
  8. import java.awt.*;
  9. import java.awt.event.KeyEvent;
  10. import java.awt.event.FocusEvent;
  11. import java.awt.event.InputEvent;
  12. import java.beans.PropertyChangeEvent;
  13. import java.io.Reader;
  14. import javax.swing.*;
  15. import javax.swing.border.*;
  16. import javax.swing.event.*;
  17. import javax.swing.text.*;
  18. import javax.swing.plaf.*;
  19. /**
  20. * Basis of a look and feel for a JTextField.
  21. * <p>
  22. * <strong>Warning:</strong>
  23. * Serialized objects of this class will not be compatible with
  24. * future Swing releases. The current serialization support is
  25. * appropriate for short term storage or RMI between applications running
  26. * the same version of Swing. As of 1.4, support for long term storage
  27. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  28. * has been added to the <code>java.beans</code> package.
  29. * Please see {@link java.beans.XMLEncoder}.
  30. *
  31. * @author Timothy Prinzing
  32. * @version 1.92 03/17/03
  33. */
  34. public class BasicTextFieldUI extends BasicTextUI {
  35. /**
  36. * Creates a UI for a JTextField.
  37. *
  38. * @param c the text field
  39. * @return the UI
  40. */
  41. public static ComponentUI createUI(JComponent c) {
  42. return new BasicTextFieldUI();
  43. }
  44. /**
  45. * Creates a new BasicTextFieldUI.
  46. */
  47. public BasicTextFieldUI() {
  48. super();
  49. }
  50. public void installUI(JComponent c) {
  51. super.installUI(c);
  52. editableChanged(c, ((JTextComponent)c).isEditable());
  53. }
  54. /**
  55. * This method gets called when a bound property is changed
  56. * on the associated JTextComponent. This is a hook
  57. * which UI implementations may change to reflect how the
  58. * UI displays bound properties of JTextComponent subclasses.
  59. *
  60. * @param evt the property change event
  61. */
  62. protected void propertyChange(PropertyChangeEvent evt) {
  63. if (evt.getPropertyName().equals("editable")) {
  64. JComponent source = (JComponent)evt.getSource();
  65. Color background = source.getBackground();
  66. boolean editable = ((Boolean)evt.getNewValue()).booleanValue();
  67. editableChanged( source, editable);
  68. }
  69. }
  70. private void editableChanged(JComponent c, boolean editable) {
  71. Color background = c.getBackground();
  72. if (background instanceof UIResource) {
  73. if (editable == false) {
  74. c.setBackground(UIManager.getColor("TextField.inactiveBackground"));
  75. } else {
  76. c.setBackground(UIManager.getColor("TextField.background"));
  77. }
  78. }
  79. }
  80. /**
  81. * Fetches the name used as a key to lookup properties through the
  82. * UIManager. This is used as a prefix to all the standard
  83. * text properties.
  84. *
  85. * @return the name ("TextField")
  86. */
  87. protected String getPropertyPrefix() {
  88. return "TextField";
  89. }
  90. /**
  91. * Creates a view (FieldView) based on an element.
  92. *
  93. * @param elem the element
  94. * @return the view
  95. */
  96. public View create(Element elem) {
  97. Document doc = elem.getDocument();
  98. Object i18nFlag = doc.getProperty("i18n"/*AbstractDocument.I18NProperty*/);
  99. if ((i18nFlag != null) && i18nFlag.equals(Boolean.TRUE)) {
  100. // To support bidirectional text, we build a more heavyweight
  101. // representation of the field.
  102. String kind = elem.getName();
  103. if (kind != null) {
  104. if (kind.equals(AbstractDocument.ContentElementName)) {
  105. return new GlyphView(elem);
  106. } else if (kind.equals(AbstractDocument.ParagraphElementName)) {
  107. return new I18nFieldView(elem);
  108. }
  109. }
  110. // this shouldn't happen, should probably throw in this case.
  111. }
  112. return new FieldView(elem);
  113. }
  114. /**
  115. * A field view that support bidirectional text via the
  116. * support provided by ParagraphView.
  117. */
  118. static class I18nFieldView extends ParagraphView {
  119. I18nFieldView(Element elem) {
  120. super(elem);
  121. }
  122. /**
  123. * Fetch the constraining span to flow against for
  124. * the given child index. There is no limit for
  125. * a field since it scrolls, so this is implemented to
  126. * return <code>Integer.MAX_VALUE</code>.
  127. */
  128. public int getFlowSpan(int index) {
  129. return Integer.MAX_VALUE;
  130. }
  131. protected void setJustification(int j) {
  132. // Justification is done in adjustAllocation(), so disable
  133. // ParagraphView's justification handling by doing nothing here.
  134. }
  135. static boolean isLeftToRight( java.awt.Component c ) {
  136. return c.getComponentOrientation().isLeftToRight();
  137. }
  138. /**
  139. * Adjusts the allocation given to the view
  140. * to be a suitable allocation for a text field.
  141. * If the view has been allocated more than the
  142. * preferred span vertically, the allocation is
  143. * changed to be centered vertically. Horizontally
  144. * the view is adjusted according to the horizontal
  145. * alignment property set on the associated JTextField
  146. * (if that is the type of the hosting component).
  147. *
  148. * @param a the allocation given to the view, which may need
  149. * to be adjusted.
  150. * @return the allocation that the superclass should use.
  151. */
  152. Shape adjustAllocation(Shape a) {
  153. if (a != null) {
  154. Rectangle bounds = a.getBounds();
  155. int vspan = (int) getPreferredSpan(Y_AXIS);
  156. int hspan = (int) getPreferredSpan(X_AXIS);
  157. if (bounds.height != vspan) {
  158. int slop = bounds.height - vspan;
  159. bounds.y += slop / 2;
  160. bounds.height -= slop;
  161. }
  162. // horizontal adjustments
  163. Component c = getContainer();
  164. if (c instanceof JTextField) {
  165. JTextField field = (JTextField) c;
  166. BoundedRangeModel vis = field.getHorizontalVisibility();
  167. int max = Math.max(hspan, bounds.width);
  168. int value = vis.getValue();
  169. int extent = Math.min(max, bounds.width - 1);
  170. if ((value + extent) > max) {
  171. value = max - extent;
  172. }
  173. vis.setRangeProperties(value, extent, vis.getMinimum(),
  174. max, false);
  175. if (hspan < bounds.width) {
  176. // horizontally align the interior
  177. int slop = bounds.width - 1 - hspan;
  178. int align = ((JTextField)c).getHorizontalAlignment();
  179. if(isLeftToRight(c)) {
  180. if(align==LEADING) {
  181. align = LEFT;
  182. }
  183. else if(align==TRAILING) {
  184. align = RIGHT;
  185. }
  186. }
  187. else {
  188. if(align==LEADING) {
  189. align = RIGHT;
  190. }
  191. else if(align==TRAILING) {
  192. align = LEFT;
  193. }
  194. }
  195. switch (align) {
  196. case SwingConstants.CENTER:
  197. bounds.x += slop / 2;
  198. bounds.width -= slop;
  199. break;
  200. case SwingConstants.RIGHT:
  201. bounds.x += slop;
  202. bounds.width -= slop;
  203. break;
  204. }
  205. } else {
  206. // adjust the allocation to match the bounded range.
  207. bounds.width = hspan;
  208. bounds.x -= vis.getValue();
  209. }
  210. }
  211. return bounds;
  212. }
  213. return null;
  214. }
  215. /**
  216. * Update the visibility model with the associated JTextField
  217. * (if there is one) to reflect the current visibility as a
  218. * result of changes to the document model. The bounded
  219. * range properties are updated. If the view hasn't yet been
  220. * shown the extent will be zero and we just set it to be full
  221. * until determined otherwise.
  222. */
  223. void updateVisibilityModel() {
  224. Component c = getContainer();
  225. if (c instanceof JTextField) {
  226. JTextField field = (JTextField) c;
  227. BoundedRangeModel vis = field.getHorizontalVisibility();
  228. int hspan = (int) getPreferredSpan(X_AXIS);
  229. int extent = vis.getExtent();
  230. int maximum = Math.max(hspan, extent);
  231. extent = (extent == 0) ? maximum : extent;
  232. int value = maximum - extent;
  233. int oldValue = vis.getValue();
  234. if ((oldValue + extent) > maximum) {
  235. oldValue = maximum - extent;
  236. }
  237. value = Math.max(0, Math.min(value, oldValue));
  238. vis.setRangeProperties(value, extent, 0, maximum, false);
  239. }
  240. }
  241. // --- View methods -------------------------------------------
  242. /**
  243. * Renders using the given rendering surface and area on that surface.
  244. * The view may need to do layout and create child views to enable
  245. * itself to render into the given allocation.
  246. *
  247. * @param g the rendering surface to use
  248. * @param a the allocated region to render into
  249. *
  250. * @see View#paint
  251. */
  252. public void paint(Graphics g, Shape a) {
  253. Rectangle r = (Rectangle) a;
  254. g.clipRect(r.x, r.y, r.width, r.height);
  255. super.paint(g, adjustAllocation(a));
  256. }
  257. /**
  258. * Determines the resizability of the view along the
  259. * given axis. A value of 0 or less is not resizable.
  260. *
  261. * @param axis View.X_AXIS or View.Y_AXIS
  262. * @return the weight -> 1 for View.X_AXIS, else 0
  263. */
  264. public int getResizeWeight(int axis) {
  265. if (axis == View.X_AXIS) {
  266. return 1;
  267. }
  268. return 0;
  269. }
  270. /**
  271. * Provides a mapping from the document model coordinate space
  272. * to the coordinate space of the view mapped to it.
  273. *
  274. * @param pos the position to convert >= 0
  275. * @param a the allocated region to render into
  276. * @return the bounding box of the given position
  277. * @exception BadLocationException if the given position does not
  278. * represent a valid location in the associated document
  279. * @see View#modelToView
  280. */
  281. public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
  282. return super.modelToView(pos, adjustAllocation(a), b);
  283. }
  284. /**
  285. * Provides a mapping from the document model coordinate space
  286. * to the coordinate space of the view mapped to it.
  287. *
  288. * @param p0 the position to convert >= 0
  289. * @param b0 the bias toward the previous character or the
  290. * next character represented by p0, in case the
  291. * position is a boundary of two views.
  292. * @param p1 the position to convert >= 0
  293. * @param b1 the bias toward the previous character or the
  294. * next character represented by p1, in case the
  295. * position is a boundary of two views.
  296. * @param a the allocated region to render into
  297. * @return the bounding box of the given position is returned
  298. * @exception BadLocationException if the given position does
  299. * not represent a valid location in the associated document
  300. * @exception IllegalArgumentException for an invalid bias argument
  301. * @see View#viewToModel
  302. */
  303. public Shape modelToView(int p0, Position.Bias b0,
  304. int p1, Position.Bias b1, Shape a)
  305. throws BadLocationException
  306. {
  307. return super.modelToView(p0, b0, p1, b1, adjustAllocation(a));
  308. }
  309. /**
  310. * Provides a mapping from the view coordinate space to the logical
  311. * coordinate space of the model.
  312. *
  313. * @param fx the X coordinate >= 0.0f
  314. * @param fy the Y coordinate >= 0.0f
  315. * @param a the allocated region to render into
  316. * @return the location within the model that best represents the
  317. * given point in the view
  318. * @see View#viewToModel
  319. */
  320. public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias) {
  321. return super.viewToModel(fx, fy, adjustAllocation(a), bias);
  322. }
  323. /**
  324. * Gives notification that something was inserted into the document
  325. * in a location that this view is responsible for.
  326. *
  327. * @param changes the change information from the associated document
  328. * @param a the current allocation of the view
  329. * @param f the factory to use to rebuild if the view has children
  330. * @see View#insertUpdate
  331. */
  332. public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
  333. super.insertUpdate(changes, adjustAllocation(a), f);
  334. updateVisibilityModel();
  335. }
  336. /**
  337. * Gives notification that something was removed from the document
  338. * in a location that this view is responsible for.
  339. *
  340. * @param changes the change information from the associated document
  341. * @param a the current allocation of the view
  342. * @param f the factory to use to rebuild if the view has children
  343. * @see View#removeUpdate
  344. */
  345. public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
  346. super.removeUpdate(changes, adjustAllocation(a), f);
  347. updateVisibilityModel();
  348. }
  349. }
  350. }