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