1. /*
  2. * @(#)ComponentView.java 1.40 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.text;
  11. import java.awt.*;
  12. import javax.swing.SwingUtilities;
  13. import javax.swing.event.*;
  14. import javax.swing.OverlayLayout;
  15. /**
  16. * Component decorator that implements the view interface. The
  17. * entire element is used to represent the component. This acts
  18. * as a gateway from the display-only View implementations to
  19. * interactive lightweight components (ie it allows components
  20. * to be embedded into the View hierarchy).
  21. *
  22. * @author Timothy Prinzing
  23. * @version 1.40 02/02/00
  24. */
  25. public class ComponentView extends View {
  26. /**
  27. * Creates a new ComponentView object.
  28. *
  29. * @param elem the element to decorate
  30. */
  31. public ComponentView(Element elem) {
  32. super(elem);
  33. }
  34. /**
  35. * Create the component that is associated with
  36. * this view. This will be called when it has
  37. * been determined that a new component is needed.
  38. * This would result from a call to setParent or
  39. * as a result of being notified that attributes
  40. * have changed.
  41. */
  42. protected Component createComponent() {
  43. AttributeSet attr = getElement().getAttributes();
  44. Component comp = StyleConstants.getComponent(attr);
  45. return comp;
  46. }
  47. /**
  48. * Fetch the component associated with the view.
  49. */
  50. public final Component getComponent() {
  51. return createdC;
  52. }
  53. // --- View methods ---------------------------------------------
  54. /**
  55. * The real paint behavior occurs naturally from the association
  56. * that the component has with its parent container (the same
  57. * container hosting this view). This is implemented to do nothing.
  58. *
  59. * @param g the graphics context
  60. * @param a the shape
  61. * @see View#paint
  62. */
  63. public void paint(Graphics g, Shape a) {
  64. }
  65. /**
  66. * Determines the preferred span for this view along an
  67. * axis. This is implemented to return the value
  68. * returned by Component.getPreferredSize along the
  69. * axis of interest.
  70. *
  71. * @param axis may be either View.X_AXIS or View.Y_AXIS
  72. * @returns the span the view would like to be rendered into >= 0.
  73. * Typically the view is told to render into the span
  74. * that is returned, although there is no guarantee.
  75. * The parent may choose to resize or break the view.
  76. * @exception IllegalArgumentException for an invalid axis
  77. */
  78. public float getPreferredSpan(int axis) {
  79. if ((axis != X_AXIS) && (axis != Y_AXIS)) {
  80. throw new IllegalArgumentException("Invalid axis: " + axis);
  81. }
  82. if (c != null) {
  83. Dimension size = c.getPreferredSize();
  84. if (axis == View.X_AXIS) {
  85. return size.width;
  86. } else {
  87. return size.height;
  88. }
  89. }
  90. return 0;
  91. }
  92. /**
  93. * Determines the minimum span for this view along an
  94. * axis. This is implemented to return the value
  95. * returned by Component.getMinimumSize along the
  96. * axis of interest.
  97. *
  98. * @param axis may be either View.X_AXIS or View.Y_AXIS
  99. * @returns the span the view would like to be rendered into >= 0.
  100. * Typically the view is told to render into the span
  101. * that is returned, although there is no guarantee.
  102. * The parent may choose to resize or break the view.
  103. * @exception IllegalArgumentException for an invalid axis
  104. */
  105. public float getMinimumSpan(int axis) {
  106. if ((axis != X_AXIS) && (axis != Y_AXIS)) {
  107. throw new IllegalArgumentException("Invalid axis: " + axis);
  108. }
  109. if (c != null) {
  110. Dimension size = c.getMinimumSize();
  111. if (axis == View.X_AXIS) {
  112. return size.width;
  113. } else {
  114. return size.height;
  115. }
  116. }
  117. return 0;
  118. }
  119. /**
  120. * Determines the maximum span for this view along an
  121. * axis. This is implemented to return the value
  122. * returned by Component.getMaximumSize along the
  123. * axis of interest.
  124. *
  125. * @param axis may be either View.X_AXIS or View.Y_AXIS
  126. * @returns the span the view would like to be rendered into >= 0.
  127. * Typically the view is told to render into the span
  128. * that is returned, although there is no guarantee.
  129. * The parent may choose to resize or break the view.
  130. * @exception IllegalArgumentException for an invalid axis
  131. */
  132. public float getMaximumSpan(int axis) {
  133. if ((axis != X_AXIS) && (axis != Y_AXIS)) {
  134. throw new IllegalArgumentException("Invalid axis: " + axis);
  135. }
  136. if (c != null) {
  137. Dimension size = c.getMaximumSize();
  138. if (axis == View.X_AXIS) {
  139. return size.width;
  140. } else {
  141. return size.height;
  142. }
  143. }
  144. return 0;
  145. }
  146. /**
  147. * Determines the desired alignment for this view along an
  148. * axis. This is implemented to give the alignment of the
  149. * embedded component.
  150. *
  151. * @param axis may be either View.X_AXIS or View.Y_AXIS
  152. * @returns the desired alignment. This should be a value
  153. * between 0.0 and 1.0 where 0 indicates alignment at the
  154. * origin and 1.0 indicates alignment to the full span
  155. * away from the origin. An alignment of 0.5 would be the
  156. * center of the view.
  157. */
  158. public float getAlignment(int axis) {
  159. if (c != null) {
  160. switch (axis) {
  161. case View.X_AXIS:
  162. return c.getAlignmentX();
  163. case View.Y_AXIS:
  164. return c.getAlignmentY();
  165. }
  166. }
  167. return super.getAlignment(axis);
  168. }
  169. /**
  170. * Sets the size of the view. This is implemented
  171. * to do nothing since the component itself will get
  172. * its size established by the LayoutManager installed
  173. * on the hosting Container.
  174. *
  175. * @param width the width >= 0
  176. * @param height the height >= 0
  177. */
  178. public void setSize(float width, float height) {
  179. }
  180. /**
  181. * Sets the parent for a child view.
  182. * The parent calls this on the child to tell it who its
  183. * parent is, giving the view access to things like
  184. * the hosting Container. The superclass behavior is
  185. * executed, followed by a call to createComponent if
  186. * a component has not yet been created and the embedded
  187. * components parent is set to the value returned by
  188. * <code>getContainer</code>.
  189. * <p>
  190. * The changing of the component hierarhcy will
  191. * touch the component lock, which is the one thing
  192. * that is not safe from the View hierarchy. Therefore,
  193. * this functionality is executed immediately if on the
  194. * event thread, or is queued on the event queue if
  195. * called from another thread (notification of change
  196. * from an asynchronous update).
  197. *
  198. * @param p the parent
  199. */
  200. public void setParent(View p) {
  201. super.setParent(p);
  202. if (SwingUtilities.isEventDispatchThread()) {
  203. setComponentParent();
  204. } else {
  205. Runnable callSetComponentParent = new Runnable() {
  206. public void run() {
  207. Document doc = getDocument();
  208. try {
  209. if (doc instanceof AbstractDocument) {
  210. ((AbstractDocument)doc).readLock();
  211. }
  212. setComponentParent();
  213. Container host = getContainer();
  214. if (host != null) {
  215. preferenceChanged(null, true, true);
  216. host.repaint();
  217. }
  218. } finally {
  219. if (doc instanceof AbstractDocument) {
  220. ((AbstractDocument)doc).readUnlock();
  221. }
  222. }
  223. }
  224. };
  225. SwingUtilities.invokeLater(callSetComponentParent);
  226. }
  227. }
  228. /**
  229. * Set the parent of the embedded component
  230. * with assurance that it is thread-safe.
  231. */
  232. void setComponentParent() {
  233. View p = getParent();
  234. if (p != null) {
  235. Container parent = getContainer();
  236. if (parent != null) {
  237. if (c == null) {
  238. // try to build a component
  239. Component comp = createComponent();
  240. if (comp != null) {
  241. createdC = comp;
  242. c = new Invalidator(comp);
  243. }
  244. }
  245. if (c != null) {
  246. if (c.getParent() == null) {
  247. // components associated with the View tree are added
  248. // to the hosting container with the View as a constraint.
  249. parent.add(c, this);
  250. }
  251. }
  252. }
  253. }
  254. }
  255. /**
  256. * Provides a mapping from the coordinate space of the model to
  257. * that of the view.
  258. *
  259. * @param pos the position to convert >= 0
  260. * @param a the allocated region to render into
  261. * @return the bounding box of the given position is returned
  262. * @exception BadLocationException if the given position does not
  263. * represent a valid location in the associated document
  264. * @see View#modelToView
  265. */
  266. public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
  267. int p0 = getStartOffset();
  268. int p1 = getEndOffset();
  269. if ((pos >= p0) && (pos <= p1)) {
  270. Rectangle r = a.getBounds();
  271. if (pos == p1) {
  272. r.x += r.width;
  273. }
  274. r.width = 0;
  275. return r;
  276. }
  277. throw new BadLocationException(pos + " not in range " + p0 + "," + p1, pos);
  278. }
  279. /**
  280. * Provides a mapping from the view coordinate space to the logical
  281. * coordinate space of the model.
  282. *
  283. * @param x the X coordinate >= 0
  284. * @param y the Y coordinate >= 0
  285. * @param a the allocated region to render into
  286. * @return the location within the model that best represents
  287. * the given point in the view
  288. * @see View#viewToModel
  289. */
  290. public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
  291. Rectangle alloc = (Rectangle) a;
  292. if (x < alloc.x + (alloc.width / 2)) {
  293. bias[0] = Position.Bias.Forward;
  294. return getStartOffset();
  295. }
  296. bias[0] = Position.Bias.Backward;
  297. return getEndOffset();
  298. }
  299. // --- member variables ------------------------------------------------
  300. private Component createdC;
  301. private Component c;
  302. /**
  303. * This class feeds the invalidate back to the
  304. * hosting View. This is needed to get the View
  305. * hierarchy to consider giving the component
  306. * a different size (i.e. layout may have been
  307. * cached between the associated view and the
  308. * container hosting this component).
  309. */
  310. class Invalidator extends Container {
  311. Invalidator(Component child) {
  312. setLayout(new OverlayLayout(this));
  313. add(child);
  314. min = child.getMinimumSize();
  315. pref = child.getPreferredSize();
  316. max = child.getMaximumSize();
  317. yalign = child.getAlignmentY();
  318. xalign = child.getAlignmentX();
  319. }
  320. /**
  321. * The components invalid layout needs
  322. * to be propagated through the view hierarchy
  323. * so the views (which position the component)
  324. * can have their layout recomputed.
  325. */
  326. public void invalidate() {
  327. super.invalidate();
  328. min = super.getMinimumSize();
  329. pref = super.getPreferredSize();
  330. max = super.getMaximumSize();
  331. yalign = super.getAlignmentY();
  332. xalign = super.getAlignmentX();
  333. if (getParent() != null) {
  334. preferenceChanged(null, true, true);
  335. }
  336. }
  337. /**
  338. * Shows or hides this component depending on the value of parameter
  339. * <code>b</code>.
  340. * @param <code>b</code> If <code>true</code>, shows this component;
  341. * otherwise, hides this component.
  342. * @see #isVisible
  343. * @since JDK1.1
  344. */
  345. public void setVisible(boolean b) {
  346. super.setVisible(b);
  347. Component child = this.getComponent(0);
  348. child.setVisible(b);
  349. }
  350. public Dimension getMinimumSize() {
  351. return min;
  352. }
  353. public Dimension getPreferredSize() {
  354. return pref;
  355. }
  356. public Dimension getMaximumSize() {
  357. return max;
  358. }
  359. public float getAlignmentX() {
  360. return xalign;
  361. }
  362. public float getAlignmentY() {
  363. return yalign;
  364. }
  365. Dimension min;
  366. Dimension pref;
  367. Dimension max;
  368. float yalign;
  369. float xalign;
  370. }
  371. }