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