001 /*
002 * Copyright 2008-2015 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.codehaus.griffon.runtime.swing;
017
018 import griffon.core.ApplicationEvent;
019 import griffon.core.GriffonApplication;
020 import griffon.core.env.ApplicationPhase;
021 import griffon.swing.SwingWindowDisplayHandler;
022 import griffon.swing.SwingWindowManager;
023 import griffon.util.GriffonNameUtils;
024 import org.codehaus.griffon.runtime.core.view.AbstractWindowManager;
025 import org.slf4j.Logger;
026 import org.slf4j.LoggerFactory;
027
028 import javax.annotation.Nonnull;
029 import javax.inject.Inject;
030 import javax.inject.Named;
031 import javax.swing.JInternalFrame;
032 import javax.swing.WindowConstants;
033 import javax.swing.event.InternalFrameAdapter;
034 import javax.swing.event.InternalFrameEvent;
035 import java.awt.Window;
036 import java.awt.event.ComponentAdapter;
037 import java.awt.event.ComponentEvent;
038 import java.awt.event.WindowAdapter;
039 import java.awt.event.WindowEvent;
040 import java.util.Collection;
041 import java.util.Collections;
042 import java.util.LinkedHashMap;
043 import java.util.Map;
044
045 import static griffon.util.GriffonNameUtils.requireNonBlank;
046 import static java.util.Arrays.asList;
047 import static java.util.Collections.unmodifiableCollection;
048 import static java.util.Objects.requireNonNull;
049
050 /**
051 * @author Andres Almiray
052 * @since 2.0.0
053 */
054 public class DefaultSwingWindowManager extends AbstractWindowManager<Window> implements SwingWindowManager {
055 private static final Logger LOG = LoggerFactory.getLogger(DefaultSwingWindowManager.class);
056 private final WindowHelper windowHelper = new WindowHelper();
057 private final ComponentHelper componentHelper = new ComponentHelper();
058 private final InternalFrameHelper internalFrameHelper = new InternalFrameHelper();
059 private final Map<String, JInternalFrame> internalFrames = Collections.synchronizedMap(new LinkedHashMap<String, JInternalFrame>());
060 private boolean hideBeforeHandler = false;
061
062 @Inject
063 @Nonnull
064 public DefaultSwingWindowManager(@Nonnull GriffonApplication application, @Nonnull @Named("windowDisplayHandler") SwingWindowDisplayHandler windowDisplayHandler) {
065 super(application, windowDisplayHandler);
066 requireNonNull(application.getEventRouter(), "Argument 'application.eventRouter' must not be null");
067 }
068
069 /**
070 * Finds a JInternalFrame by name.
071 *
072 * @param name the value of the name: property
073 * @return a JInternalFrame if a match is found, null otherwise.
074 * @since 2.0.0
075 */
076 public JInternalFrame findInternalFrame(String name) {
077 if (!GriffonNameUtils.isBlank(name)) {
078 for (JInternalFrame internalFrame : internalFrames.values()) {
079 if (name.equals(internalFrame.getName())) return internalFrame;
080 }
081 }
082 return null;
083 }
084
085 /**
086 * Returns the list of internal frames managed by this manager.
087 *
088 * @return a List of currently managed internal frames
089 * @since 2.0.0
090 */
091 public Collection<JInternalFrame> getInternalFrames() {
092 return unmodifiableCollection(internalFrames.values());
093 }
094
095 /**
096 * Registers an internal frame on this manager if an only if the internal frame is not null
097 * and it's not registered already.
098 *
099 * @param name the value of the of the Window's name
100 * @param internalFrame the internal frame to be added to the list of managed internal frames
101 * @since 2.0.0
102 */
103 public void attach(@Nonnull String name, @Nonnull JInternalFrame internalFrame) {
104 requireNonBlank(name, ERROR_NAME_BLANK);
105 requireNonNull(internalFrame, ERROR_WINDOW_NULL);
106 if (internalFrames.containsKey(name)) {
107 JInternalFrame window2 = internalFrames.get(name);
108 if (window2 != internalFrame) {
109 detach(name);
110 }
111 }
112
113 doAttach(internalFrame);
114
115 if (LOG.isDebugEnabled()) {
116 LOG.debug("Attaching internal frame with name: '" + name + "' at index " + internalFrames.size() + " " + internalFrame);
117 }
118 internalFrames.put(name, internalFrame);
119 event(ApplicationEvent.WINDOW_ATTACHED, asList(name, internalFrame));
120 }
121
122 protected void doAttach(@Nonnull JInternalFrame internalFrame) {
123 internalFrame.addInternalFrameListener(internalFrameHelper);
124 internalFrame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
125 }
126
127 /**
128 * Removes the internal frame from the list of manages internal frames if and only if it
129 * is registered with this manager.
130 *
131 * @param name the value of the of the Window's name
132 * @since 2.0.0
133 */
134 public void detach(@Nonnull String name) {
135 requireNonBlank(name, ERROR_NAME_BLANK);
136 if (internalFrames.containsKey(name)) {
137 JInternalFrame window = internalFrames.get(name);
138
139 doDetach(window);
140
141 if (LOG.isDebugEnabled()) {
142 LOG.debug("Detaching internalFrame with name: '" + name + "' " + window);
143 }
144 internalFrames.remove(name);
145 event(ApplicationEvent.WINDOW_DETACHED, asList(name, window));
146 }
147 }
148
149 protected void doDetach(@Nonnull JInternalFrame internalFrame) {
150 internalFrame.removeInternalFrameListener(internalFrameHelper);
151 }
152
153 /**
154 * Shows the internal frame.<p>
155 * This method is executed <b>SYNCHRONOUSLY</b> in the UI thread.
156 *
157 * @param internalFrame the internal frame to show
158 * @since 2.0.0
159 */
160 public void show(@Nonnull final JInternalFrame internalFrame) {
161 requireNonNull(internalFrame, ERROR_WINDOW_NULL);
162 if (!internalFrames.containsValue(internalFrame)) {
163 return;
164 }
165
166 String windowName = null;
167 int windowIndex = -1;
168 synchronized (internalFrames) {
169 int i = 0;
170 for (Map.Entry<String, JInternalFrame> entry : internalFrames.entrySet()) {
171 if (entry.getValue() == internalFrame) {
172 windowName = entry.getKey();
173 windowIndex = i;
174 break;
175 }
176 i++;
177 }
178 }
179
180 final String name = windowName;
181 final int index = windowIndex;
182
183 getApplication().getUIThreadManager().runInsideUIAsync(new Runnable() {
184 public void run() {
185 if (LOG.isDebugEnabled()) {
186 LOG.debug("Showing window with name: '" + name + "' at index " + index + " " + internalFrame);
187 }
188 //noinspection ConstantConditions
189 resolveSwingWindowDisplayHandler().show(name, internalFrame);
190 }
191 });
192 }
193
194 /**
195 * Hides the internal frame.<p>
196 * This method is executed <b>SYNCHRONOUSLY</b> in the UI thread.
197 *
198 * @param internalFrame the internal frame to hide
199 * @since 2.0.0
200 */
201 public void hide(@Nonnull final JInternalFrame internalFrame) {
202 requireNonNull(internalFrame, ERROR_WINDOW_NULL);
203 if (!internalFrames.containsValue(internalFrame)) {
204 return;
205 }
206
207 String windowName = null;
208 int windowIndex = -1;
209 synchronized (internalFrames) {
210 int i = 0;
211 for (Map.Entry<String, JInternalFrame> entry : internalFrames.entrySet()) {
212 if (entry.getValue() == internalFrame) {
213 windowName = entry.getKey();
214 windowIndex = i;
215 break;
216 }
217 i++;
218 }
219 }
220
221 final String name = windowName;
222 final int index = windowIndex;
223
224 getApplication().getUIThreadManager().runInsideUIAsync(new Runnable() {
225 public void run() {
226 if (LOG.isDebugEnabled()) {
227 LOG.debug("Hiding window with name: '" + name + "' at index " + index + " " + internalFrame);
228 }
229 //noinspection ConstantConditions
230 resolveSwingWindowDisplayHandler().hide(name, internalFrame);
231 }
232 });
233 }
234
235 /**
236 * Should the window be hidden before all ShutdownHandlers be called ?
237 *
238 * @return current value
239 */
240 public boolean isHideBeforeHandler() {
241 return hideBeforeHandler;
242 }
243
244 /**
245 * Set if the window should be hidden before all ShutdownHandler be called.
246 *
247 * @param hideBeforeHandler new value
248 */
249 public void setHideBeforeHandler(boolean hideBeforeHandler) {
250 this.hideBeforeHandler = hideBeforeHandler;
251 }
252
253 @Nonnull
254 protected SwingWindowDisplayHandler resolveSwingWindowDisplayHandler() {
255 return (SwingWindowDisplayHandler) resolveWindowDisplayHandler();
256 }
257
258 @Override
259 protected void doAttach(@Nonnull Window window) {
260 requireNonNull(window, ERROR_WINDOW_NULL);
261 window.addWindowListener(windowHelper);
262 window.addComponentListener(componentHelper);
263 }
264
265 @Override
266 protected void doDetach(@Nonnull Window window) {
267 requireNonNull(window, ERROR_WINDOW_NULL);
268 window.removeWindowListener(windowHelper);
269 window.removeComponentListener(componentHelper);
270 }
271
272 @Override
273 protected boolean isWindowVisible(@Nonnull Window window) {
274 requireNonNull(window, ERROR_WINDOW_NULL);
275 return window.isVisible();
276 }
277
278 /**
279 * WindowAdapter that optionally invokes hide() when the window is about to be closed.
280 *
281 * @author Andres Almiray
282 */
283 private class WindowHelper extends WindowAdapter {
284 public void windowClosing(WindowEvent event) {
285 if (getApplication().getPhase() == ApplicationPhase.SHUTDOWN) {
286 return;
287 }
288 int visibleWindows = countVisibleWindows();
289
290 if (isHideBeforeHandler() || visibleWindows > 0) {
291 hide(event.getWindow());
292 }
293
294 if (visibleWindows <= 1 && isAutoShutdown()) {
295 LOG.debug("Attempting to shutdown application");
296 if (!getApplication().shutdown()) show(event.getWindow());
297 }
298 }
299 }
300
301 /**
302 * ComponentAdapter that triggers application events when a window is shown/hidden.
303 *
304 * @author Andres Almiray
305 */
306 private class ComponentHelper extends ComponentAdapter {
307 /**
308 * Triggers a <tt>WindowShown</tt> event with the window as sole argument
309 */
310 public void componentShown(ComponentEvent event) {
311 event(ApplicationEvent.WINDOW_SHOWN, asList(event.getSource()));
312 }
313
314 /**
315 * Triggers a <tt>WindowHidden</tt> event with the window as sole argument
316 */
317 public void componentHidden(ComponentEvent event) {
318 event(ApplicationEvent.WINDOW_HIDDEN, asList(event.getSource()));
319 }
320 }
321
322 /**
323 * InternalFrameAdapter that triggers application events when a window is shown/hidden,
324 * it also invokes hide() when the window is about to be closed.
325 *
326 * @author Andres Almiray
327 */
328 private class InternalFrameHelper extends InternalFrameAdapter {
329 public void internalFrameClosing(InternalFrameEvent event) {
330 hide(event.getInternalFrame());
331 }
332
333 /**
334 * Triggers a <tt>WindowShown</tt> event with the internal frame as sole argument
335 */
336 public void internalFrameOpened(InternalFrameEvent event) {
337 event(ApplicationEvent.WINDOW_SHOWN, asList(event.getSource()));
338
339 }
340
341 /**
342 * Triggers a <tt>WindowHidden</tt> event with the internal frame as sole argument
343 */
344 public void internalFrameClosed(InternalFrameEvent event) {
345 event(ApplicationEvent.WINDOW_HIDDEN, asList(event.getSource()));
346 }
347 }
348 }
|