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