/* * Copyright 2009-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package griffon.javafx; import griffon.core.ApplicationPhase; import griffon.core.GriffonApplication; import griffon.core.ShutdownHandler; import griffon.util.ConfigUtils; import griffon.util.GriffonNameUtils; import groovy.util.ConfigObject; import javafx.event.EventHandler; import javafx.stage.Window; import javafx.stage.WindowEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** * Controls a set of windows that belong to the application. * <p/> * Windows that are controlled by a WindowManager can be shown/hidden using a custom strategy ({@code WindowDisplayHandler}) * * @author Andres Almiray * @see griffon.javafx.WindowDisplayHandler */ public final class WindowManager implements ShutdownHandler { private static final Logger LOG = LoggerFactory.getLogger(WindowManager.class); private final JavaFXGriffonApplication app; private final OnWindowHidingHelper onWindowHiding = new OnWindowHidingHelper(); private final OnWindowShownHelper onWindowShown = new OnWindowShownHelper(); private final OnWindowHiddenHelper onWindowHidden = new OnWindowHiddenHelper(); private final Map<String, Window> windows = new ConcurrentHashMap<String, Window>(); /** * Creates a new WindowManager tied to an specific application. * * @param app an application */ public WindowManager(JavaFXGriffonApplication app) { this.app = app; } /** * Finds a Window by name. * * @param name the value of the name: property * @return a Window if a match is found, null otherwise. */ public Window findWindow(String name) { if (!GriffonNameUtils.isBlank(name)) { return windows.get(name); } return null; } public String findWindowName(Window window) { if (window != null) { for (Map.Entry<String, Window> entry : windows.entrySet()) { if (entry.getValue() == window) { return entry.getKey(); } } } return null; } /** * Finds the Window that should be displayed during the Ready phase of an application. * <p/> * The WindowManager expects a configuration flag <code>javafx.windowManager.startingWindow</code> to be present in order to * determine which Window will be displayed during the Ready phase. If no configuration is found the WindowManmager will pick the * first Window found in the list of managed windows. * <p/> * The configuration flag accepts two value types: * <ul> * <li>a String that defines the name of the Window. You must make sure the Window has a matching name property.</li> * <li>a Number that defines the index of the Window in the list of managed windows.</li> * </ul> * * @return a Window that matches the given criteria or null if no match is found. */ public Window getStartingWindow() { Window window = null; Object value = ConfigUtils.getConfigValue(app.getConfig(), "javafx.windowManager.startingWindow"); if (LOG.isDebugEnabled()) { LOG.debug("javafx.windowManager.startingWindow configured to " + value); } if (value == null || value instanceof ConfigObject) { if (windows.size() > 0) { if (LOG.isDebugEnabled()) { LOG.debug("No startingWindow configured, selecting the first one in the list of windows"); } window = windows.values().iterator().next(); } } else if (value instanceof String) { String windowName = (String) value; if (LOG.isDebugEnabled()) { LOG.debug("Selecting window " + windowName + " as starting window"); } window = findWindow(windowName); } else if (value instanceof Number) { int index = ((Number) value).intValue(); if (index >= 0 && index < windows.size()) { if (LOG.isDebugEnabled()) { LOG.debug("Selecting window at index " + index + " as starting window"); } int i = 0; for (Iterator<Window> iter = windows.values().iterator(); iter.hasNext(); i++) { if (i == index) { window = iter.next(); break; } } } } if (LOG.isDebugEnabled()) { LOG.debug("Starting window is " + window); } return window; } /** * Returns the list of windows managed by this manager. * * @return a List of currently managed windows */ public Collection<Window> getWindows() { return Collections.<Window>unmodifiableCollection(windows.values()); } /** * Registers a window on this manager if an only if the window is not null * and it's not registered already. * * @param window the window to be added to the list of managed windows */ public void attach(String name, Window window) { if (window == null || windows.values().contains(window)) { return; } window.setOnHiding(onWindowHiding); window.setOnShown(onWindowShown); window.setOnHidden(onWindowHidden); if (LOG.isDebugEnabled()) { LOG.debug("Attaching window with name: '" + name + " " + window); } windows.put(name, window); } /** * Removes the window from the list of manages windows if and only if it * is registered with this manager. * * @param window the window to be removed */ public void detach(Window window) { if (window == null) { return; } if (windows.values().contains(window)) { window.setOnHiding(null); window.setOnShown(null); window.setOnHidden(null); String name = findWindowName(window); if (LOG.isDebugEnabled()) { LOG.debug("Detaching window with name: '" + name + " " + window); } windows.remove(name); } } /** * Shows the window. * <p/> * This method is executed <b>SYNCHRONOUSLY</b> in the UI thread. * * @param window the window to show */ public void show(final Window window) { if (window == null) { return; } app.execInsideUISync(new Runnable() { public void run() { if (LOG.isDebugEnabled()) { LOG.debug("Showing window with name: '" + findWindowName(window) + " " + window); } app.resolveWindowDisplayHandler().show(window, app); } }); } /** * Hides the window. * <p/> * This method is executed <b>SYNCHRONOUSLY</b> in the UI thread. * * @param window the window to hide */ public void hide(final Window window) { if (window == null) { return; } app.execInsideUISync(new Runnable() { public void run() { if (LOG.isDebugEnabled()) { LOG.debug("Hiding window with name: '" + findWindowName(window) + " " + window); } app.resolveWindowDisplayHandler().hide(window, app); } }); } public boolean canShutdown(GriffonApplication app) { return true; } /** * Hides all visible windows */ public void onShutdown(GriffonApplication app) { for (Window window : windows.values()) { if (window.isShowing()) { hide(window); } } } public void handleClose(Window widget) { if (app.getPhase() == ApplicationPhase.SHUTDOWN) { return; } int visibleWindows = 0; for (Window window : windows.values()) { if (window.isShowing()) { visibleWindows++; } } Boolean autoShutdown = (Boolean) app.getConfig().flatten().get("application.autoShutdown"); if (visibleWindows <= 1 && autoShutdown != null && autoShutdown.booleanValue()) { if (!app.shutdown()) show((Window) widget); } } /** * WindowAdapter that invokes hide() when the window is about to be closed. * * @author Andres Almiray */ private class OnWindowHidingHelper implements EventHandler<WindowEvent> { public void handle(WindowEvent event) { hide((Window) event.getSource()); handleClose((Window) event.getSource()); } } /** * Listener that triggers application events when a window is shown. * * @author Andres Almiray */ private class OnWindowShownHelper implements EventHandler<WindowEvent> { /** * Triggers a <tt>WindowShown</tt> event with the window as sole argument */ public void handle(WindowEvent windowEvent) { app.event(GriffonApplication.Event.WINDOW_SHOWN.getName(), Arrays.asList(windowEvent.getSource())); } } /** * Listener that triggers application events when a window is hidden. * * @author Andres Almiray */ private class OnWindowHiddenHelper implements EventHandler<WindowEvent> { /** * Triggers a <tt>WindowHidden</tt> event with the window as sole argument */ public void handle(WindowEvent windowEvent) { app.event(GriffonApplication.Event.WINDOW_HIDDEN.getName(), Arrays.asList(windowEvent.getSource())); } } }