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