| 
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 }
 |