1. /*
  2. * @(#)PopupFactory.java 1.19 03/01/31
  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;
  8. import java.applet.Applet;
  9. import java.awt.*;
  10. import java.awt.event.WindowAdapter;
  11. import java.awt.event.WindowEvent;
  12. import java.util.ArrayList;
  13. import java.util.HashMap;
  14. import java.util.List;
  15. import java.util.Map;
  16. /**
  17. * <code>PopupFactory</code>, as the name implies, is used to obtain
  18. * instances of <code>Popup</code>s. <code>Popup</code>s are used to
  19. * display a <code>Component</code> above all other <code>Component</code>s
  20. * in a particular containment hierarchy. The general contract is that
  21. * once you have obtained a <code>Popup</code> from a
  22. * <code>PopupFactory</code>, you must invoke <code>hide</code> on the
  23. * <code>Popup</code>. The typical usage is:
  24. * <pre>
  25. * PopupFactory factory = PopupFactory.getSharedInstance();
  26. * Popup popup = factory.getPopup(owner, contents, x, y);
  27. * popup.show();
  28. * ...
  29. * popup.hide();
  30. * </pre>
  31. *
  32. * @see Popup
  33. *
  34. * @version 1.19 01/31/03
  35. * @since 1.4
  36. */
  37. public class PopupFactory {
  38. /**
  39. * The shared instanceof <code>PopupFactory</code> is per
  40. * <code>AppContext</code>. This is the key used in the
  41. * <code>AppContext</code> to locate the <code>PopupFactory</code>.
  42. */
  43. private static final Object SharedInstanceKey =
  44. new StringBuffer("PopupFactory.SharedInstanceKey");
  45. /**
  46. * Max number of items to store in any one particular cache.
  47. */
  48. private static final int MAX_CACHE_SIZE = 5;
  49. /**
  50. * Key used to indicate a light weight popup should be used.
  51. */
  52. static final int LIGHT_WEIGHT_POPUP = 0;
  53. /**
  54. * Key used to indicate a medium weight Popup should be used.
  55. */
  56. static final int MEDIUM_WEIGHT_POPUP = 1;
  57. /*
  58. * Key used to indicate a heavy weight Popup should be used.
  59. */
  60. static final int HEAVY_WEIGHT_POPUP = 2;
  61. /**
  62. * Default type of Popup to create.
  63. */
  64. private int popupType = LIGHT_WEIGHT_POPUP;
  65. /**
  66. * Sets the <code>AppContext</code> specific <code>PopupFactory</code>.
  67. * This will throw an <code>IllegalArgumentException</code> if
  68. * <code>factory</code> is null.
  69. *
  70. * @param factory Shared PopupFactory
  71. * @exception IllegalArgumentException if <code>factory</code> is null
  72. */
  73. public static void setSharedInstance(PopupFactory factory) {
  74. if (factory == null) {
  75. throw new IllegalArgumentException("PopupFactory can not be null");
  76. }
  77. SwingUtilities.appContextPut(SharedInstanceKey, factory);
  78. }
  79. /**
  80. * Returns the shared <code>PopupFactory</code> which can be used
  81. * to obtain <code>Popup</code>s.
  82. *
  83. * @return Shared PopupFactory
  84. */
  85. public static PopupFactory getSharedInstance() {
  86. PopupFactory factory = (PopupFactory)SwingUtilities.appContextGet(
  87. SharedInstanceKey);
  88. if (factory == null) {
  89. factory = new PopupFactory();
  90. setSharedInstance(factory);
  91. }
  92. return factory;
  93. }
  94. /**
  95. * Provides a hint as to the type of <code>Popup</code> that should
  96. * be created.
  97. */
  98. void setPopupType(int type) {
  99. popupType = type;
  100. }
  101. /**
  102. * Returns the preferred type of Popup to create.
  103. */
  104. int getPopupType() {
  105. return popupType;
  106. }
  107. /**
  108. * Creates a <code>Popup</code> for the Component <code>owner</code>
  109. * containing the Component <code>contents</code>. <code>owner</code>
  110. * is used to determine which <code>Window</code> the new
  111. * <code>Popup</code> will parent the <code>Component</code> the
  112. * <code>Popup</code> creates to. A null <code>owner</code> implies there
  113. * is no valid parent. <code>x</code> and
  114. * <code>y</code> specify the preferred initial location to place
  115. * the <code>Popup</code> at. Based on screen size, or other paramaters,
  116. * the <code>Popup</code> may not display at <code>x</code> and
  117. * <code>y</code>.
  118. *
  119. * @param owner Component mouse coordinates are relative to, may be null
  120. * @param contents Contents of the Popup
  121. * @param x Initial x screen coordinate
  122. * @param y Initial y screen coordinate
  123. * @exception IllegalArgumentException if contents is null
  124. * @return Popup containing Contents
  125. */
  126. public Popup getPopup(Component owner, Component contents,
  127. int x, int y) throws IllegalArgumentException {
  128. if (contents == null) {
  129. throw new IllegalArgumentException(
  130. "Popup.getPopup must be passed non-null contents");
  131. }
  132. int popupType = getPopupType(owner, contents, x, y);
  133. Popup popup = getPopup(owner, contents, x, y, popupType);
  134. if (popup == null) {
  135. // Didn't fit, force to heavy.
  136. popup = getPopup(owner, contents, x, y, HEAVY_WEIGHT_POPUP);
  137. }
  138. return popup;
  139. }
  140. /**
  141. * Returns the popup type to use for the specified parameters.
  142. */
  143. private int getPopupType(Component owner, Component contents,
  144. int ownerX, int ownerY) {
  145. int popupType = getPopupType();
  146. if (owner == null || invokerInHeavyWeightPopup(owner)) {
  147. popupType = HEAVY_WEIGHT_POPUP;
  148. }
  149. else if (popupType == LIGHT_WEIGHT_POPUP &&
  150. !(contents instanceof JToolTip) &&
  151. !(contents instanceof JPopupMenu)) {
  152. popupType = MEDIUM_WEIGHT_POPUP;
  153. }
  154. return popupType;
  155. }
  156. /**
  157. * Obtains the appropriate <code>Popup</code> based on
  158. * <code>popupType</code>.
  159. */
  160. private Popup getPopup(Component owner, Component contents,
  161. int ownerX, int ownerY, int popupType) {
  162. if (GraphicsEnvironment.isHeadless()) {
  163. return getHeadlessPopup(owner, contents, ownerX, ownerY);
  164. }
  165. switch(popupType) {
  166. case LIGHT_WEIGHT_POPUP:
  167. return getLightWeightPopup(owner, contents, ownerX, ownerY);
  168. case MEDIUM_WEIGHT_POPUP:
  169. return getMediumWeightPopup(owner, contents, ownerX, ownerY);
  170. case HEAVY_WEIGHT_POPUP:
  171. return getHeavyWeightPopup(owner, contents, ownerX, ownerY);
  172. }
  173. return null;
  174. }
  175. /**
  176. * Creates a headless popup
  177. */
  178. private Popup getHeadlessPopup(Component owner, Component contents,
  179. int ownerX, int ownerY) {
  180. return HeadlessPopup.getHeadlessPopup(owner, contents, ownerX, ownerY);
  181. }
  182. /**
  183. * Creates a light weight popup.
  184. */
  185. private Popup getLightWeightPopup(Component owner, Component contents,
  186. int ownerX, int ownerY) {
  187. return LightWeightPopup.getLightWeightPopup(owner, contents, ownerX,
  188. ownerY);
  189. }
  190. /**
  191. * Creates a medium weight popup.
  192. */
  193. private Popup getMediumWeightPopup(Component owner, Component contents,
  194. int ownerX, int ownerY) {
  195. return MediumWeightPopup.getMediumWeightPopup(owner, contents,
  196. ownerX, ownerY);
  197. }
  198. /**
  199. * Creates a heavy weight popup.
  200. */
  201. private Popup getHeavyWeightPopup(Component owner, Component contents,
  202. int ownerX, int ownerY) {
  203. if (GraphicsEnvironment.isHeadless()) {
  204. return getMediumWeightPopup(owner, contents, ownerX, ownerY);
  205. }
  206. return HeavyWeightPopup.getHeavyWeightPopup(owner, contents, ownerX,
  207. ownerY);
  208. }
  209. /**
  210. * Returns true if the Component <code>i</code> inside a heavy weight
  211. * <code>Popup</code>.
  212. */
  213. private boolean invokerInHeavyWeightPopup(Component i) {
  214. if (i != null) {
  215. Container parent;
  216. for(parent = i.getParent() ; parent != null ; parent =
  217. parent.getParent()) {
  218. if (parent instanceof Popup.HeavyWeightWindow) {
  219. return true;
  220. }
  221. }
  222. }
  223. return false;
  224. }
  225. /**
  226. * Popup implementation that uses a Window as the popup.
  227. */
  228. private static class HeavyWeightPopup extends Popup {
  229. private static final Object heavyWeightPopupCacheKey =
  230. new StringBuffer("PopupFactory.heavyWeightPopupCache");
  231. /**
  232. * Returns either a new or recycled <code>Popup</code> containing
  233. * the specified children.
  234. */
  235. static Popup getHeavyWeightPopup(Component owner, Component contents,
  236. int ownerX, int ownerY) {
  237. Window window = (owner != null) ? SwingUtilities.
  238. getWindowAncestor(owner) : null;
  239. HeavyWeightPopup popup = null;
  240. if (window != null) {
  241. popup = getRecycledHeavyWeightPopup(window);
  242. }
  243. boolean focusPopup = false;
  244. if(contents != null && contents.isFocusable()) {
  245. if(contents instanceof JPopupMenu) {
  246. JPopupMenu jpm = (JPopupMenu) contents;
  247. Component popComps[] = jpm.getComponents();
  248. for(int i=0;i<popComps.length;i++) {
  249. if(!(popComps[i] instanceof MenuElement) &&
  250. !(popComps[i] instanceof JSeparator)) {
  251. focusPopup = true;
  252. break;
  253. }
  254. }
  255. }
  256. }
  257. if (popup == null ||
  258. ((JWindow) popup.getComponent())
  259. .getFocusableWindowState() != focusPopup) {
  260. if(popup != null) {
  261. // The recycled popup can't serve us well
  262. // dispose it and create new one
  263. popup._dispose();
  264. }
  265. popup = new HeavyWeightPopup();
  266. }
  267. popup.reset(owner, contents, ownerX, ownerY);
  268. if(focusPopup) {
  269. JWindow wnd = (JWindow) popup.getComponent();
  270. wnd.setFocusableWindowState(true);
  271. // Set window name. We need this in BasicPopupMenuUI
  272. // to identify focusable popup window.
  273. wnd.setName("###focusableSwingPopup###");
  274. }
  275. return popup;
  276. }
  277. /**
  278. * Returns a previously disposed heavy weight <code>Popup</code>
  279. * associated with <code>window</code>. This will return null if
  280. * there is no <code>HeavyWeightPopup</code> associated with
  281. * <code>window</code>.
  282. */
  283. private static HeavyWeightPopup getRecycledHeavyWeightPopup(Window w) {
  284. synchronized (HeavyWeightPopup.class) {
  285. List cache;
  286. Map heavyPopupCache = getHeavyWeightPopupCache();
  287. if (heavyPopupCache.containsKey(w)) {
  288. cache = (List)heavyPopupCache.get(w);
  289. } else {
  290. return null;
  291. }
  292. int c;
  293. if ((c = cache.size()) > 0) {
  294. HeavyWeightPopup r = (HeavyWeightPopup)cache.get(0);
  295. cache.remove(0);
  296. return r;
  297. }
  298. return null;
  299. }
  300. }
  301. /**
  302. * Returns the cache to use for heavy weight popups. Maps from
  303. * <code>Window</code> to a <code>List</code> of
  304. * <code>HeavyWeightPopup</code>s.
  305. */
  306. private static Map getHeavyWeightPopupCache() {
  307. synchronized (HeavyWeightPopup.class) {
  308. Map cache = (Map)SwingUtilities.appContextGet(
  309. heavyWeightPopupCacheKey);
  310. if (cache == null) {
  311. cache = new HashMap(2);
  312. SwingUtilities.appContextPut(heavyWeightPopupCacheKey,
  313. cache);
  314. }
  315. return cache;
  316. }
  317. }
  318. /**
  319. * Recycles the passed in <code>HeavyWeightPopup</code>.
  320. */
  321. private static void recycleHeavyWeightPopup(HeavyWeightPopup popup) {
  322. synchronized (HeavyWeightPopup.class) {
  323. List cache;
  324. Object window = SwingUtilities.getWindowAncestor(
  325. popup.getComponent());
  326. Map heavyPopupCache = getHeavyWeightPopupCache();
  327. if (window instanceof Popup.DefaultFrame ||
  328. !((Window)window).isVisible()) {
  329. // If the Window isn't visible, we don't cache it as we
  330. // likely won't ever get a windowClosed event to clean up.
  331. // We also don't cache DefaultFrames as this indicates
  332. // there wasn't a valid Window parent, and thus we don't
  333. // know when to clean up.
  334. popup._dispose();
  335. return;
  336. } else if (heavyPopupCache.containsKey(window)) {
  337. cache = (List)heavyPopupCache.get(window);
  338. } else {
  339. cache = new ArrayList();
  340. heavyPopupCache.put(window, cache);
  341. // Clean up if the Window is closed
  342. final Window w = (Window)window;
  343. w.addWindowListener(new WindowAdapter() {
  344. public void windowClosed(WindowEvent e) {
  345. List popups;
  346. synchronized(HeavyWeightPopup.class) {
  347. Map heavyPopupCache2 =
  348. getHeavyWeightPopupCache();
  349. popups = (List)heavyPopupCache2.remove(w);
  350. }
  351. if (popups != null) {
  352. for (int counter = popups.size() - 1;
  353. counter >= 0; counter--) {
  354. ((HeavyWeightPopup)popups.get(counter)).
  355. _dispose();
  356. }
  357. }
  358. }
  359. });
  360. }
  361. if(cache.size() < MAX_CACHE_SIZE) {
  362. cache.add(popup);
  363. } else {
  364. popup._dispose();
  365. }
  366. }
  367. }
  368. //
  369. // Popup methods
  370. //
  371. public void hide() {
  372. super.hide();
  373. recycleHeavyWeightPopup(this);
  374. }
  375. /**
  376. * As we recycle the <code>Window</code>, we don't want to dispose it,
  377. * thus this method does nothing, instead use <code>_dipose</code>
  378. * which will handle the disposing.
  379. */
  380. void dispose() {
  381. }
  382. void _dispose() {
  383. super.dispose();
  384. }
  385. }
  386. /**
  387. * ContainerPopup consolidates the common code used in the light/medium
  388. * weight implementations of <code>Popup</code>.
  389. */
  390. private static class ContainerPopup extends Popup {
  391. /** Component we are to be added to. */
  392. Component owner;
  393. /** Desired x location. */
  394. int x;
  395. /** Desired y location. */
  396. int y;
  397. public void hide() {
  398. Component component = getComponent();
  399. if (component != null) {
  400. Container parent = component.getParent();
  401. if (parent != null) {
  402. Rectangle bounds = component.getBounds();
  403. parent.remove(component);
  404. parent.repaint(bounds.x, bounds.y, bounds.width,
  405. bounds.height);
  406. }
  407. }
  408. owner = null;
  409. }
  410. public void pack() {
  411. Component component = getComponent();
  412. if (component != null) {
  413. component.setSize(component.getPreferredSize());
  414. }
  415. }
  416. void reset(Component owner, Component contents, int ownerX,
  417. int ownerY) {
  418. if ((owner instanceof JFrame) || (owner instanceof JDialog) ||
  419. (owner instanceof JWindow)) {
  420. // Force the content to be added to the layered pane, otherwise
  421. // we'll get an exception when adding to the RootPaneContainer.
  422. owner = ((RootPaneContainer)owner).getLayeredPane();
  423. }
  424. super.reset(owner, contents, ownerX, ownerY);
  425. x = ownerX;
  426. y = ownerY;
  427. this.owner = owner;
  428. }
  429. boolean overlappedByOwnedWindow() {
  430. Component component = getComponent();
  431. if(owner != null && component != null) {
  432. Window w = SwingUtilities.getWindowAncestor(owner);
  433. if(w == null)
  434. return false;
  435. Window[] ownedWindows = w.getOwnedWindows();
  436. if(ownedWindows != null) {
  437. Rectangle bnd = component.getBounds();
  438. for(int i=0; i<ownedWindows.length;i++) {
  439. if(bnd.intersects(ownedWindows[i].getBounds())) {
  440. return true;
  441. }
  442. }
  443. }
  444. }
  445. return false;
  446. }
  447. /**
  448. * Returns true if the Popup can fit on the screen.
  449. */
  450. boolean fitsOnScreen() {
  451. Component component = getComponent();
  452. if (owner != null && component != null) {
  453. Container parent;
  454. int width = component.getWidth();
  455. int height = component.getHeight();
  456. for(parent = owner.getParent(); parent != null ;
  457. parent = parent.getParent()) {
  458. if (parent instanceof JFrame ||
  459. parent instanceof JDialog ||
  460. parent instanceof JWindow) {
  461. Rectangle r = parent.getBounds();
  462. Insets i = parent.getInsets();
  463. r.x += i.left;
  464. r.y += i.top;
  465. r.width -= (i.left + i.right);
  466. r.height -= (i.top + i.bottom);
  467. return SwingUtilities.isRectangleContainingRectangle(
  468. r, new Rectangle(x, y, width, height));
  469. } else if (parent instanceof JApplet) {
  470. Rectangle r = parent.getBounds();
  471. Point p = parent.getLocationOnScreen();
  472. r.x = p.x;
  473. r.y = p.y;
  474. return SwingUtilities.isRectangleContainingRectangle(
  475. r, new Rectangle(x, y, width, height));
  476. } else if (parent instanceof Window ||
  477. parent instanceof Applet) {
  478. // No suitable swing component found
  479. break;
  480. }
  481. }
  482. }
  483. return false;
  484. }
  485. }
  486. /**
  487. * Popup implementation that is used in headless environment.
  488. */
  489. private static class HeadlessPopup extends ContainerPopup {
  490. static Popup getHeadlessPopup(Component owner, Component contents,
  491. int ownerX, int ownerY) {
  492. HeadlessPopup popup = new HeadlessPopup();
  493. popup.reset(owner, contents, ownerX, ownerY);
  494. return popup;
  495. }
  496. Component createComponent(Component owner) {
  497. return new Panel(new BorderLayout());
  498. }
  499. public void show() {
  500. }
  501. public void hide() {
  502. }
  503. }
  504. /**
  505. * Popup implementation that uses a JPanel as the popup.
  506. */
  507. private static class LightWeightPopup extends ContainerPopup {
  508. private static final Object lightWeightPopupCacheKey =
  509. new StringBuffer("PopupFactory.lightPopupCache");
  510. /**
  511. * Returns a light weight <code>Popup</code> implementation. If
  512. * the <code>Popup</code> needs more space that in available in
  513. * <code>owner</code>, this will return null.
  514. */
  515. static Popup getLightWeightPopup(Component owner, Component contents,
  516. int ownerX, int ownerY) {
  517. LightWeightPopup popup = getRecycledLightWeightPopup();
  518. if (popup == null) {
  519. popup = new LightWeightPopup();
  520. }
  521. popup.reset(owner, contents, ownerX, ownerY);
  522. if (!popup.fitsOnScreen() ||
  523. popup.overlappedByOwnedWindow()) {
  524. popup.hide();
  525. return null;
  526. }
  527. return popup;
  528. }
  529. /**
  530. * Returns the cache to use for heavy weight popups.
  531. */
  532. private static List getLightWeightPopupCache() {
  533. List cache = (List)SwingUtilities.appContextGet(
  534. lightWeightPopupCacheKey);
  535. if (cache == null) {
  536. cache = new ArrayList();
  537. SwingUtilities.appContextPut(lightWeightPopupCacheKey, cache);
  538. }
  539. return cache;
  540. }
  541. /**
  542. * Recycles the LightWeightPopup <code>popup</code>.
  543. */
  544. private static void recycleLightWeightPopup(LightWeightPopup popup) {
  545. synchronized (LightWeightPopup.class) {
  546. List lightPopupCache = getLightWeightPopupCache();
  547. if (lightPopupCache.size() < MAX_CACHE_SIZE) {
  548. lightPopupCache.add(popup);
  549. }
  550. }
  551. }
  552. /**
  553. * Returns a previously used <code>LightWeightPopup</code>, or null
  554. * if none of the popups have been recycled.
  555. */
  556. private static LightWeightPopup getRecycledLightWeightPopup() {
  557. synchronized (LightWeightPopup.class) {
  558. List lightPopupCache = getLightWeightPopupCache();
  559. int c;
  560. if((c = lightPopupCache.size()) > 0) {
  561. LightWeightPopup r = (LightWeightPopup)lightPopupCache.
  562. get(0);
  563. lightPopupCache.remove(0);
  564. return r;
  565. }
  566. return null;
  567. }
  568. }
  569. //
  570. // Popup methods
  571. //
  572. public void hide() {
  573. super.hide();
  574. Container component = (Container)getComponent();
  575. component.removeAll();
  576. recycleLightWeightPopup(this);
  577. }
  578. public void show() {
  579. Container parent = null;
  580. if (owner != null) {
  581. parent = (owner instanceof Container? (Container)owner : owner.getParent());
  582. }
  583. // Try to find a JLayeredPane and Window to add
  584. for (Container p = parent; p != null; p = p.getParent()) {
  585. if (p instanceof JRootPane) {
  586. if (p.getParent() instanceof JInternalFrame) {
  587. continue;
  588. }
  589. parent = ((JRootPane)p).getLayeredPane();
  590. // Continue, so that if there is a higher JRootPane, we'll
  591. // pick it up.
  592. } else if(p instanceof Window) {
  593. if (parent == null) {
  594. parent = p;
  595. }
  596. break;
  597. } else if (p instanceof JApplet) {
  598. // Painting code stops at Applets, we don't want
  599. // to add to a Component above an Applet otherwise
  600. // you'll never see it painted.
  601. break;
  602. }
  603. }
  604. Point p = SwingUtilities.convertScreenLocationToParent(parent, x,
  605. y);
  606. Component component = getComponent();
  607. component.setLocation(p.x, p.y);
  608. if (parent instanceof JLayeredPane) {
  609. ((JLayeredPane)parent).add(component,
  610. JLayeredPane.POPUP_LAYER, 0);
  611. } else {
  612. parent.add(component);
  613. }
  614. }
  615. Component createComponent(Component owner) {
  616. JComponent component = new JPanel(new BorderLayout(), true);
  617. component.setOpaque(true);
  618. return component;
  619. }
  620. //
  621. // Local methods
  622. //
  623. /**
  624. * Resets the <code>Popup</code> to an initial state.
  625. */
  626. void reset(Component owner, Component contents, int ownerX,
  627. int ownerY) {
  628. super.reset(owner, contents, ownerX, ownerY);
  629. JComponent component = (JComponent)getComponent();
  630. component.setLocation(ownerX, ownerY);
  631. component.add(contents, BorderLayout.CENTER);
  632. contents.invalidate();
  633. pack();
  634. }
  635. }
  636. /**
  637. * Popup implementation that uses a Panel as the popup.
  638. */
  639. private static class MediumWeightPopup extends ContainerPopup {
  640. private static final Object mediumWeightPopupCacheKey =
  641. new StringBuffer("PopupFactory.mediumPopupCache");
  642. /** Child of the panel. The contents are added to this. */
  643. private JRootPane rootPane;
  644. /**
  645. * Returns a medium weight <code>Popup</code> implementation. If
  646. * the <code>Popup</code> needs more space that in available in
  647. * <code>owner</code>, this will return null.
  648. */
  649. static Popup getMediumWeightPopup(Component owner, Component contents,
  650. int ownerX, int ownerY) {
  651. MediumWeightPopup popup = getRecycledMediumWeightPopup();
  652. if (popup == null) {
  653. popup = new MediumWeightPopup();
  654. }
  655. popup.reset(owner, contents, ownerX, ownerY);
  656. if (!popup.fitsOnScreen() ||
  657. popup.overlappedByOwnedWindow()) {
  658. popup.hide();
  659. return null;
  660. }
  661. return popup;
  662. }
  663. /**
  664. * Returns the cache to use for medium weight popups.
  665. */
  666. private static List getMediumWeightPopupCache() {
  667. List cache = (List)SwingUtilities.appContextGet(
  668. mediumWeightPopupCacheKey);
  669. if (cache == null) {
  670. cache = new ArrayList();
  671. SwingUtilities.appContextPut(mediumWeightPopupCacheKey, cache);
  672. }
  673. return cache;
  674. }
  675. /**
  676. * Recycles the MediumWeightPopup <code>popup</code>.
  677. */
  678. private static void recycleMediumWeightPopup(MediumWeightPopup popup) {
  679. synchronized (MediumWeightPopup.class) {
  680. List mediumPopupCache = getMediumWeightPopupCache();
  681. if (mediumPopupCache.size() < MAX_CACHE_SIZE) {
  682. mediumPopupCache.add(popup);
  683. }
  684. }
  685. }
  686. /**
  687. * Returns a previously used <code>MediumWeightPopup</code>, or null
  688. * if none of the popups have been recycled.
  689. */
  690. private static MediumWeightPopup getRecycledMediumWeightPopup() {
  691. synchronized (MediumWeightPopup.class) {
  692. java.util.List mediumPopupCache =
  693. getMediumWeightPopupCache();
  694. int c;
  695. if ((c=mediumPopupCache.size()) > 0) {
  696. MediumWeightPopup r = (MediumWeightPopup)mediumPopupCache.
  697. get(0);
  698. mediumPopupCache.remove(0);
  699. return r;
  700. }
  701. return null;
  702. }
  703. }
  704. //
  705. // Popup
  706. //
  707. public void hide() {
  708. super.hide();
  709. rootPane.getContentPane().removeAll();
  710. recycleMediumWeightPopup(this);
  711. }
  712. public void show() {
  713. Component component = getComponent();
  714. Container parent = null;
  715. if (owner != null) {
  716. parent = owner.getParent();
  717. }
  718. /*
  719. Find the top level window,
  720. if it has a layered pane,
  721. add to that, otherwise
  722. add to the window. */
  723. while (!(parent instanceof Window || parent instanceof Applet) &&
  724. (parent!=null)) {
  725. parent = parent.getParent();
  726. }
  727. // Set the visibility to false before adding to workaround a
  728. // bug in Solaris in which the Popup gets added at the wrong
  729. // location, which will result in a mouseExit, which will then
  730. // result in the ToolTip being removed.
  731. if (parent instanceof RootPaneContainer) {
  732. parent = ((RootPaneContainer)parent).getLayeredPane();
  733. Point p = SwingUtilities.convertScreenLocationToParent(parent,
  734. x, y);
  735. component.setVisible(false);
  736. component.setLocation(p.x, p.y);
  737. ((JLayeredPane)parent).add(component, JLayeredPane.POPUP_LAYER,
  738. 0);
  739. } else {
  740. Point p = SwingUtilities.convertScreenLocationToParent(parent,
  741. x, y);
  742. component.setLocation(p.x, p.y);
  743. component.setVisible(false);
  744. parent.add(component);
  745. }
  746. component.setVisible(true);
  747. }
  748. Component createComponent(Component owner) {
  749. Panel component = new Panel(new BorderLayout());
  750. rootPane = new JRootPane();
  751. component.add(rootPane, BorderLayout.CENTER);
  752. return component;
  753. }
  754. /**
  755. * Resets the <code>Popup</code> to an initial state.
  756. */
  757. void reset(Component owner, Component contents, int ownerX,
  758. int ownerY) {
  759. super.reset(owner, contents, ownerX, ownerY);
  760. Component component = getComponent();
  761. component.setLocation(ownerX, ownerY);
  762. rootPane.getContentPane().add(contents, BorderLayout.CENTER);
  763. contents.invalidate();
  764. component.validate();
  765. pack();
  766. }
  767. }
  768. }