001 /*
002 * Copyright 2008-2017 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 @Override
172 public void detach(@Nonnull String name) {
173 requireNonBlank(name, ERROR_NAME_BLANK);
174 if (internalFrames.containsKey(name)) {
175 JInternalFrame window = internalFrames.get(name);
176
177 doDetach(window);
178
179 if (LOG.isDebugEnabled()) {
180 LOG.debug("Detaching internalFrame with name: '" + name + "' " + window);
181 }
182 internalFrames.remove(name);
183 event(ApplicationEvent.WINDOW_DETACHED, asList(name, window));
184 }
185 }
186
187 protected void doDetach(@Nonnull JInternalFrame internalFrame) {
188 internalFrame.removeInternalFrameListener(internalFrameHelper);
189 }
190
191 /**
192 * Shows the internal frame.<p>
193 * This method is executed <b>SYNCHRONOUSLY</b> in the UI thread.
194 *
195 * @param internalFrame the internal frame to show
196 * @since 2.0.0
197 */
198 public void show(@Nonnull final JInternalFrame internalFrame) {
199 requireNonNull(internalFrame, ERROR_WINDOW_NULL);
200 if (!internalFrames.containsValue(internalFrame)) {
201 return;
202 }
203
204 String windowName = null;
205 int windowIndex = -1;
206 synchronized (internalFrames) {
207 int i = 0;
208 for (Map.Entry<String, JInternalFrame> entry : internalFrames.entrySet()) {
209 if (entry.getValue() == internalFrame) {
210 windowName = entry.getKey();
211 windowIndex = i;
212 break;
213 }
214 i++;
215 }
216 }
217
218 final String name = windowName;
219 final int index = windowIndex;
220
221 getApplication().getUIThreadManager().runInsideUIAsync(new Runnable() {
222 public void run() {
223 if (LOG.isDebugEnabled()) {
224 LOG.debug("Showing window with name: '" + name + "' at index " + index + " " + internalFrame);
225 }
226 //noinspection ConstantConditions
227 resolveSwingWindowDisplayHandler().show(name, internalFrame);
228 }
229 });
230 }
231
232 /**
233 * Hides the internal frame.<p>
234 * This method is executed <b>SYNCHRONOUSLY</b> in the UI thread.
235 *
236 * @param internalFrame the internal frame to hide
237 * @since 2.0.0
238 */
239 public void hide(@Nonnull final JInternalFrame internalFrame) {
240 requireNonNull(internalFrame, ERROR_WINDOW_NULL);
241 if (!internalFrames.containsValue(internalFrame)) {
242 return;
243 }
244
245 String windowName = null;
246 int windowIndex = -1;
247 synchronized (internalFrames) {
248 int i = 0;
249 for (Map.Entry<String, JInternalFrame> entry : internalFrames.entrySet()) {
250 if (entry.getValue() == internalFrame) {
251 windowName = entry.getKey();
252 windowIndex = i;
253 break;
254 }
255 i++;
256 }
257 }
258
259 final String name = windowName;
260 final int index = windowIndex;
261
262 getApplication().getUIThreadManager().runInsideUIAsync(new Runnable() {
263 public void run() {
264 if (LOG.isDebugEnabled()) {
265 LOG.debug("Hiding window with name: '" + name + "' at index " + index + " " + internalFrame);
266 }
267 //noinspection ConstantConditions
268 resolveSwingWindowDisplayHandler().hide(name, internalFrame);
269 }
270 });
271 }
272
273 /**
274 * Should the window be hidden before all ShutdownHandlers be called ?
275 *
276 * @return current value
277 */
278 public boolean isHideBeforeHandler() {
279 return hideBeforeHandler;
280 }
281
282 /**
283 * Set if the window should be hidden before all ShutdownHandler be called.
284 *
285 * @param hideBeforeHandler new value
286 */
287 public void setHideBeforeHandler(boolean hideBeforeHandler) {
288 this.hideBeforeHandler = hideBeforeHandler;
289 }
290
291 @Nonnull
292 protected SwingWindowDisplayHandler resolveSwingWindowDisplayHandler() {
293 return (SwingWindowDisplayHandler) resolveWindowDisplayHandler();
294 }
295
296 @Override
297 protected void doAttach(@Nonnull Window window) {
298 requireNonNull(window, ERROR_WINDOW_NULL);
299 window.addWindowListener(windowHelper);
300 window.addComponentListener(componentHelper);
301 }
302
303 @Override
304 protected void doDetach(@Nonnull Window window) {
305 requireNonNull(window, ERROR_WINDOW_NULL);
306 window.removeWindowListener(windowHelper);
307 window.removeComponentListener(componentHelper);
308 }
309
310 @Override
311 protected boolean isWindowVisible(@Nonnull Window window) {
312 requireNonNull(window, ERROR_WINDOW_NULL);
313 return window.isVisible();
314 }
315
316 /**
317 * WindowAdapter that optionally invokes hide() when the window is about to be closed.
318 *
319 * @author Andres Almiray
320 */
321 private class WindowHelper extends WindowAdapter {
322 @Override
323 public void windowClosing(WindowEvent event) {
324 if (getApplication().getPhase() == ApplicationPhase.SHUTDOWN) {
325 return;
326 }
327 int visibleWindows = countVisibleWindows();
328
329 if (isHideBeforeHandler() || visibleWindows > 0) {
330 hide(event.getWindow());
331 }
332
333 if (visibleWindows <= 1 && isAutoShutdown()) {
334 LOG.debug("Attempting to shutdown application");
335 if (!getApplication().shutdown()) show(event.getWindow());
336 }
337 }
338 }
339
340 /**
341 * ComponentAdapter that triggers application events when a window is shown/hidden.
342 *
343 * @author Andres Almiray
344 */
345 private class ComponentHelper extends ComponentAdapter {
346 /**
347 * Triggers a <tt>WindowShown</tt> event with the window as sole argument
348 */
349 @Override
350 public void componentShown(ComponentEvent event) {
351 Window window = (Window) event.getSource();
352 event(ApplicationEvent.WINDOW_SHOWN, asList(findWindowName(window), window));
353 }
354
355 /**
356 * Triggers a <tt>WindowHidden</tt> event with the window as sole argument
357 */
358 @Override
359 public void componentHidden(ComponentEvent event) {
360 Window window = (Window) event.getSource();
361 event(ApplicationEvent.WINDOW_HIDDEN, asList(findWindowName(window), window));
362 }
363 }
364
365 /**
366 * InternalFrameAdapter that triggers application events when a window is shown/hidden,
367 * it also invokes hide() when the window is about to be closed.
368 *
369 * @author Andres Almiray
370 */
371 private class InternalFrameHelper extends InternalFrameAdapter {
372 @Override
373 public void internalFrameClosing(InternalFrameEvent event) {
374 hide(event.getInternalFrame());
375 }
376
377 /**
378 * Triggers a <tt>WindowShown</tt> event with the internal frame as sole argument
379 */
380 @Override
381 public void internalFrameOpened(InternalFrameEvent event) {
382 JInternalFrame window = (JInternalFrame) event.getSource();
383 event(ApplicationEvent.WINDOW_SHOWN, asList(findInternalWindowName(window), window));
384
385 }
386
387 /**
388 * Triggers a <tt>WindowHidden</tt> event with the internal frame as sole argument
389 */
390 @Override
391 public void internalFrameClosed(InternalFrameEvent event) {
392 JInternalFrame window = (JInternalFrame) event.getSource();
393 event(ApplicationEvent.WINDOW_HIDDEN, asList(findInternalWindowName(window), window));
394 }
395 }
396 }
|