選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

LockedLayer.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. /**
  2. * Copyright (c) 2006-2009, Alexander Potochkin
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. *
  9. * * Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * * Redistributions in binary form must reproduce the above
  12. * copyright notice, this list of conditions and the following
  13. * disclaimer in the documentation and/or other materials provided
  14. * with the distribution.
  15. * * Neither the name of the JXLayer project nor the names of its
  16. * contributors may be used to endorse or promote products derived
  17. * from this software without specific prior written permission.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  22. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  23. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  24. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  25. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  26. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  27. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  28. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. */
  31. package com.dmdirc.addons.ui_swing.components;
  32. import java.awt.Component;
  33. import java.awt.Cursor;
  34. import java.awt.Graphics;
  35. import java.awt.Graphics2D;
  36. import java.awt.KeyboardFocusManager;
  37. import java.awt.event.FocusEvent;
  38. import java.awt.event.FocusListener;
  39. import javax.swing.JComponent;
  40. import javax.swing.SwingUtilities;
  41. import org.jdesktop.jxlayer.JXLayer;
  42. import org.jdesktop.jxlayer.plaf.AbstractBufferedLayerUI;
  43. import org.jdesktop.jxlayer.plaf.effect.LayerEffect;
  44. /**
  45. * An implementation of the {@code BufferedLayerUI} which provides
  46. * a lightweight disabling for the content of its {@link JXLayer}.
  47. * This allows temporarily blocking a part of the interface
  48. * with all it subcomponents, it is also useful when some kind of action
  49. * is in progress, e.g. reading data from a database.
  50. * <p/>
  51. * When {@code true} is passed to the {@link #setLocked(boolean)},
  52. * the {@code JXLayer} of this {@code LockableLayerUI} becomes "locked".
  53. * It sets the "wait" mouse cursor and stops reacting
  54. * on mouse, keyboard and focus events.
  55. * <p/>
  56. * If {@code setLocked(boolean)} is called with {@code false} parameter
  57. * after that, the {@code JXLayer}, together with all its subcomponents,
  58. * gets back to live.
  59. * <p/>
  60. * Subclasses usually override {@link #paintLayer(Graphics2D,JXLayer)} or
  61. * {@link #getLayerEffects(JXLayer)} to implement some visual effects
  62. * when {@code JXLayer} is in locked state.
  63. * <p/>
  64. * Here is an example of using {@code LockableLayerUI}:
  65. * <pre>
  66. * JComponent myComponent = getMyComponent(); // just any component
  67. * <p/>
  68. * LockableLayerUI lockableUI = new LockableLayerUI();
  69. * JXLayer&lt;JComponent&gt; layer = new JXLayer&lt;JComponent&gt;(myComponent,
  70. * lockableUI);
  71. * <p/>
  72. * // locking the layer, use lockableUI.setLocked(false) to unlock
  73. * lockableUI.setLocked(true);
  74. * <p/>
  75. * // add the layer to a frame or a panel, like any other component
  76. * frame.add(layer);
  77. * </pre>
  78. * @param <T> Generic type
  79. */
  80. public class LockedLayer<T extends JComponent> extends
  81. AbstractBufferedLayerUI<T> {
  82. /**
  83. * A version number for this class. It should be changed whenever the class
  84. * structure is changed (or anything else that would prevent serialized
  85. * objects being unserialized with the new class).
  86. */
  87. private static final long serialVersionUID = 1;
  88. /** Are we locked. */
  89. private boolean locked;
  90. /** Most recent focus owner? */
  91. private Component recentFocusOwner;
  92. /** Cursor to show when locked. */
  93. private Cursor lockedCursor = Cursor.getPredefinedCursor(
  94. Cursor.DEFAULT_CURSOR);
  95. /** Effect to apply when locked. */
  96. private LayerEffect[] lockedEffects = new LayerEffect[0];
  97. /** Focus listener. */
  98. private final transient FocusListener focusListener = new FocusListener() {
  99. /** {@inheritDoc} */
  100. @Override
  101. public void focusGained(final FocusEvent e) {
  102. // we don't want extra repaintings
  103. // when focus comes from another window
  104. if (e.getOppositeComponent() != null) {
  105. setDirty(true);
  106. }
  107. }
  108. /** {@inheritDoc} */
  109. @Override
  110. public void focusLost(final FocusEvent e) {
  111. //Ignore
  112. }
  113. };
  114. /**
  115. * Creates a new instance of LockableUI.
  116. */
  117. public LockedLayer() {
  118. this((LayerEffect[]) null);
  119. }
  120. /**
  121. * Creates a new instance of LockableUI, passed lockedEffects will be used
  122. * for when this UI in the locked state.
  123. *
  124. * @param effects effects to be used when this UI is locked
  125. * @see #setLocked(boolean)
  126. * @see #setLockedEffects(LayerEffect...)
  127. */
  128. public LockedLayer(final LayerEffect... effects) {
  129. super();
  130. setLockedEffects(effects);
  131. }
  132. /**
  133. * {@inheritDoc}
  134. */
  135. @Override
  136. public void installUI(final JComponent c) {
  137. super.installUI(c);
  138. // we need to repaint the layer when it receives the focus
  139. // otherwise the focused component will have it on the buffer image
  140. c.addFocusListener(focusListener);
  141. }
  142. /**
  143. * {@inheritDoc}
  144. */
  145. @Override
  146. public void uninstallUI(final JComponent c) {
  147. super.uninstallUI(c);
  148. c.removeFocusListener(focusListener);
  149. }
  150. /**
  151. * Returns {@code true} if this {@code LockableLayerUI}
  152. * is in locked state and all {@link JXLayer}'s mouse, keyboard and focus
  153. * events are temporarily blocked, otherwise returns {@code false}.
  154. *
  155. * @return {@code true} if this {@code LockableLayerUI} is in locked state
  156. * and all {@code JXLayer}'s mouse, keyboard and focus events are
  157. * temporarily blocked, otherwise returns {@code false}
  158. */
  159. public boolean isLocked() {
  160. return locked;
  161. }
  162. /**
  163. * If {@code isLocked} is {@code true} then all mouse, keyboard and focus
  164. * events from the {@link JXLayer} of this {@code LockableLayerUI} will be
  165. * temporarily blocked.
  166. *
  167. * @param isLocked if {@code true} then all mouse, keyboard and focus events
  168. * from the {@code JXLayer} of this {@code LockableLayerUI} will be
  169. * temporarily blocked
  170. */
  171. public void setLocked(final boolean isLocked) {
  172. if (isLocked != isLocked()) {
  173. if (getLayer() != null) {
  174. final KeyboardFocusManager kfm = KeyboardFocusManager.
  175. getCurrentKeyboardFocusManager();
  176. final Component focusOwner = kfm.getPermanentFocusOwner();
  177. final boolean focusInLayer = focusOwner != null
  178. && SwingUtilities.isDescendingFrom(focusOwner,
  179. getLayer());
  180. if (isLocked) {
  181. if (focusInLayer && kfm.getFocusedWindow()
  182. == SwingUtilities.getWindowAncestor(getLayer())) {
  183. recentFocusOwner = focusOwner;
  184. // setDirty() will be called from the layer's
  185. //focusListener when focus already left layer's view
  186. //and hiding it in the paintLayer() won't mess the
  187. //focus up getLayer().requestFocusInWindow();
  188. } else {
  189. setDirty(true);
  190. }
  191. // the mouse cursor is set to the glassPane
  192. getLayer().getGlassPane().setCursor(getLockedCursor());
  193. } else {
  194. // show the view again
  195. getLayer().getView().setVisible(true);
  196. // restore the focus if it is still in the layer
  197. if (focusInLayer && recentFocusOwner != null) {
  198. recentFocusOwner.requestFocusInWindow();
  199. }
  200. recentFocusOwner = null;
  201. getLayer().getGlassPane().setCursor(null);
  202. }
  203. }
  204. this.locked = isLocked;
  205. firePropertyChange("locked", !isLocked, isLocked);
  206. }
  207. }
  208. // If it is locked, the buffer image will be updated
  209. // only if the layer changes its size or setDirty(true) was called
  210. /** {@inheritDoc} */
  211. @Override
  212. protected boolean isIncrementalUpdate(
  213. final JXLayer<? extends T> l) {
  214. return !isLocked();
  215. }
  216. /** {@inheritDoc} */
  217. @Override
  218. protected void paintLayer(final Graphics2D g2,
  219. final JXLayer<? extends T> l) {
  220. if (isLocked()) {
  221. // Note: this code will be called only if layer changes its size,
  222. // or setDirty(true) was called,
  223. // otherwise the previously created buffer is used
  224. l.getView().setVisible(true);
  225. l.paint(g2);
  226. // hide the layer's view component
  227. // this is the only way to disable key shortcuts
  228. // installed on its subcomponents
  229. l.getView().setVisible(false);
  230. }
  231. }
  232. /** {@inheritDoc} */
  233. @Override
  234. public void paint(final Graphics g, final JComponent c) {
  235. super.paint(g, c);
  236. // if it is not locked, we need to paint the layer as is
  237. // otherwise we also need to paint it again;
  238. // if there are any glassPane's children
  239. // they should be shown unfiltered
  240. c.paint(g);
  241. }
  242. /**
  243. * Returns the mouse cursor to be used
  244. * by this {@code LockableLayerUI} when it locked state.
  245. *
  246. * @return the mouse cursor to be used
  247. * by this {@code LockableLayerUI} when it locked state
  248. * @see #getLockedCursor()
  249. * @see #setLocked(boolean)
  250. */
  251. public Cursor getLockedCursor() {
  252. return lockedCursor;
  253. }
  254. /**
  255. * Sets the mouse cursor to be used
  256. * by this {@code LockableLayerUI} when it locked state.
  257. *
  258. * @param newCursor the mouse cursor to be used by this
  259. * {@code LockableLayerUI} when it locked state
  260. */
  261. public void setLockedCursor(final Cursor newCursor) {
  262. final Cursor oldCursor = getLockedCursor();
  263. this.lockedCursor = newCursor;
  264. firePropertyChange("lockedCursor", oldCursor, newCursor);
  265. if (isLocked()) {
  266. getLayer().getGlassPane().setCursor(newCursor);
  267. }
  268. }
  269. /**
  270. * Returns the effects to be used when this UI is locked.
  271. *
  272. * @return the effects to be used when this UI is locked
  273. * @see #setLocked(boolean)
  274. */
  275. public LayerEffect[] getLockedEffects() {
  276. final LayerEffect[] result = new LayerEffect[lockedEffects.length];
  277. System.arraycopy(lockedEffects, 0, result, 0, result.length);
  278. return result;
  279. }
  280. /**
  281. * This method returns the array of {@code LayerEffect}s
  282. * set using {@link #setLayerEffects(LayerEffect...)}
  283. * <p/>
  284. * If a {@code LockableUI} provides more extensive API
  285. * to support different {@code Effect}s depending on its state
  286. * or on the state of the passed {@code JXLayer},
  287. * this method should be overridden.
  288. *
  289. * @param l Layer to get effects from
  290. *
  291. * @return Locked layer effect
  292. *
  293. * @see #setLockedEffects (LayerEffect...)
  294. * @see #getLockedEffects()
  295. */
  296. protected LayerEffect[] getLockedEffects(
  297. final JXLayer<? extends JComponent> l) {
  298. return getLockedEffects();
  299. }
  300. /**
  301. * Sets the effects to be used when this UI is locked.
  302. *
  303. * @param effects the effects to be used when this UI is locked
  304. * @see #setLocked(boolean)
  305. */
  306. public void setLockedEffects(final LayerEffect... effects) {
  307. final LayerEffect[] layerEffect;
  308. final LayerEffect[] oldEffects = getLockedEffects();
  309. if (effects == null) {
  310. layerEffect = new LayerEffect[0];
  311. } else {
  312. layerEffect = effects;
  313. }
  314. for (LayerEffect effect : getLockedEffects()) {
  315. effect.removePropertyChangeListener(this);
  316. }
  317. this.lockedEffects = new LayerEffect[layerEffect.length];
  318. System.arraycopy(layerEffect, 0, this.lockedEffects, 0,
  319. layerEffect.length);
  320. for (LayerEffect lockedEffect : layerEffect) {
  321. lockedEffect.addPropertyChangeListener(this);
  322. }
  323. firePropertyChange("lockedEffects", oldEffects, layerEffect);
  324. }
  325. /**
  326. * {@inheritDoc}
  327. *
  328. * @param l Layer
  329. *
  330. * @return Layer effects
  331. */
  332. @Override
  333. protected LayerEffect[] getLayerEffects(final JXLayer<? extends T> l) {
  334. if (isLocked()) {
  335. return getLockedEffects(l);
  336. } else {
  337. return super.getLayerEffects(l);
  338. }
  339. }
  340. }