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