/*
GNU GENERAL PUBLIC LICENSE
Copyright (C) 2006 The Lobo Project
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
verion 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Contact info: lobochief@users.sourceforge.net
*/
/*
* Created on Mar 6, 2005
*/
package org.lobobrowser.gui;
import java.awt.Frame;
import java.awt.event.WindowEvent;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ImageIcon;
import javax.swing.WindowConstants;
import org.lobobrowser.main.ExtensionManager;
import org.lobobrowser.main.PlatformInit;
import org.lobobrowser.request.SilentUserAgentContextImpl;
import org.lobobrowser.settings.GeneralSettings;
import org.lobobrowser.ua.NavigatorWindow;
import org.lobobrowser.ua.UserAgentContext;
import org.lobobrowser.util.EventDispatch;
import org.lobobrowser.util.ID;
import org.lobobrowser.util.WeakValueHashMap;
/**
* Browser windows are created by this factory by default.
*/
public class DefaultWindowFactory implements WindowFactory {
private static final Logger logger = Logger.getLogger(DefaultWindowFactory.class.getName());
private static DefaultWindowFactory instance = new DefaultWindowFactory();
private static final String DEFAULT_ICON_URL = "res:/images/gngrLogo.png";
public final EventDispatch evtWindowShown = new EventDispatch();
// TODO: Should use an expiring cache instead of a WeakHashMap.
private final Map<String, ImageIcon> imageMap = new WeakValueHashMap<>();
private final Map<String, DefaultBrowserWindow> framesById = new WeakValueHashMap<>();
private final Set<java.awt.Frame> frames = new HashSet<>();
private final GeneralSettings generalSettings;
private volatile boolean exitWhenAllWindowsClosed = false;
protected DefaultWindowFactory() {
// One way to avoid security exceptions
this.generalSettings = GeneralSettings.getInstance();
}
public void setExitWhenAllWindowsAreClosed(final boolean flag) {
this.exitWhenAllWindowsClosed = flag;
}
public static DefaultWindowFactory getInstance() {
return instance;
}
/**
* Gets the default image icon for browser windows.
*
* @param uaContext
*/
public ImageIcon getDefaultImageIcon(final UserAgentContext uaContext) {
return this.getImageIcon(DEFAULT_ICON_URL, uaContext);
}
/**
* Gets an image icon.
*
* @param urlOrPath
* A URL or path.
* @param uaContext
*/
private ImageIcon getImageIcon(final String urlOrPath, final UserAgentContext uaContext) {
synchronized (this) {
ImageIcon icon = this.imageMap.get(urlOrPath);
if (icon == null) {
try {
final byte[] imageBytes = org.lobobrowser.request.RequestEngine.getInstance().loadBytes(urlOrPath, uaContext);
icon = new ImageIcon(imageBytes);
this.imageMap.put(urlOrPath, icon);
} catch (final Exception err) {
logger.log(Level.WARNING, "getImageIcon(): Unable to load image: " + urlOrPath, err);
}
}
return icon;
}
}
public AbstractBrowserWindow getExistingWindow(final String windowId) {
if (windowId == null) {
return null;
}
synchronized (this) {
final DefaultBrowserWindow window = this.framesById.get(windowId);
if ((window != null) && window.isDisplayable()) {
return window;
}
}
return null;
}
private AbstractBrowserWindow createBaseWindow(final String windowId, final NavigatorWindow windowContext, final boolean hasMenuBar,
final boolean hasAddressBar,
final boolean hasToolBar, final boolean hasStatusBar) {
final NavigatorWindowImpl pwc = (NavigatorWindowImpl) windowContext;
synchronized (this) {
final DefaultBrowserWindow window = new DefaultBrowserWindow(hasMenuBar, hasAddressBar, hasToolBar, hasStatusBar, pwc);
if (windowId != null) {
this.framesById.put(windowId, window);
}
window.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
if (logger.isLoggable(Level.FINE)) {
logger.fine("createBaseWindow(): Adding window listener: window=" + window + ",windowId=" + windowId);
}
window.addWindowListener(new java.awt.event.WindowAdapter() {
@Override
public void windowClosing(final WindowEvent e) {
super.windowClosing(e);
if (!window.isBoundsAssigned()) {
if (logger.isLoggable(Level.INFO)) {
logger.info("windowClosing(): Saving general settings: bounds=" + window.getBounds());
}
final GeneralSettings settings = generalSettings;
settings.setInitialWindowBounds(window.getBounds());
settings.save();
}
ExtensionManager.getInstance().shutdownExtensionsWindow(pwc);
}
@Override
public void windowClosed(final java.awt.event.WindowEvent e) {
super.windowClosed(e);
final Set<Frame> frames = DefaultWindowFactory.this.frames;
synchronized (DefaultWindowFactory.this) {
if (logger.isLoggable(Level.INFO)) {
logger.info("windowClosed(): frames.size()=" + frames.size() + ",exitWhenAllWindowsClosed=" + exitWhenAllWindowsClosed);
}
frames.remove(window);
if ((frames.size() == 0) && exitWhenAllWindowsClosed) {
logger.warning("Exiting JVM because all windows are now closed!");
PlatformInit.shutdown();
}
}
}
@Override
public void windowOpened(final WindowEvent e) {
evtWindowShown.fireEvent(null);
}
});
this.frames.add(window);
return window;
}
}
private final static boolean isPropertyTrue(final Properties properties, final String name, final boolean defaultValue) {
if (properties == null) {
return defaultValue;
}
final String value = properties.getProperty(name);
if (value == null) {
return defaultValue;
}
return "1".equals(value) || "yes".equalsIgnoreCase(value);
}
/**
* Creates, sizes a browser window, and registeres listeners that allow the
* platform to persist window settings and shut itself down when all windows
* are closed.
*/
public AbstractBrowserWindow createWindow(final String windowId, final Properties properties, final NavigatorWindow windowContext) {
final String widthText = properties == null ? null : properties.getProperty("width");
final String heightText = properties == null ? null : properties.getProperty("height");
final boolean defaultValue = (widthText == null) && (heightText == null);
final boolean hasMenuBar = isPropertyTrue(properties, "menubar", defaultValue);
final boolean hasToolBar = isPropertyTrue(properties, "toolbar", defaultValue);
final boolean hasAddressBar = isPropertyTrue(properties, "location", defaultValue);
final boolean hasStatusBar = isPropertyTrue(properties, "status", defaultValue);
final boolean isResizable = isPropertyTrue(properties, "resizable", defaultValue);
final String iconText = properties == null ? null : properties.getProperty("icon");
final String title = properties == null ? null : properties.getProperty("title");
int width = -1;
int height = -1;
if (widthText != null) {
try {
width = Integer.parseInt(widthText);
} catch (final NumberFormatException nfe) {
logger.log(Level.WARNING, "PlatformWindowContextImpl(): Unable to parse window width.", nfe);
}
}
if (heightText != null) {
try {
height = Integer.parseInt(heightText);
} catch (final NumberFormatException nfe) {
logger.log(Level.WARNING, "PlatformWindowContextImpl(): Unable to parse window height.", nfe);
}
}
final AbstractBrowserWindow window = this
.createBaseWindow(windowId, windowContext, hasMenuBar, hasAddressBar, hasToolBar, hasStatusBar);
window.setTitle(title);
final java.awt.Rectangle windowBounds = this.generalSettings.getInitialWindowBounds();
if ((width != -1) || (height != -1)) {
if (width != -1) {
windowBounds.width = width;
}
if (height != -1) {
windowBounds.height = height;
}
window.setBoundsAssigned(true);
}
ImageIcon icon = null;
final UserAgentContext uaContext = new SilentUserAgentContextImpl(windowContext.getTopFrame());
if (iconText != null) {
icon = this.getImageIcon(iconText, uaContext);
if (icon == null) {
icon = this.getDefaultImageIcon(uaContext);
}
} else {
icon = this.getDefaultImageIcon(uaContext);
}
if (icon != null) {
window.setIconImage(icon.getImage());
}
final java.awt.Dimension windowSize = windowBounds.getSize();
final java.awt.Rectangle maxBounds = java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
final int maxX = maxBounds.width - windowSize.width;
final int maxY = maxBounds.height - windowSize.height;
final int x = ID.random(0, maxX);
final int y = ID.random(0, maxY);
window.setBounds(x, y, windowSize.width, windowSize.height);
window.setResizable(isResizable);
return window;
}
public void overrideProperties(final AbstractBrowserWindow window, final Properties properties) {
final String widthText = properties.getProperty("width");
final String heightText = properties.getProperty("height");
final boolean defaultValue = (widthText == null) && (heightText == null);
final boolean isResizable = isPropertyTrue(properties, "resizable", defaultValue);
// final String iconText = properties.getProperty("icon");
// final String title = properties.getProperty("title");
int width = -1;
int height = -1;
if (widthText != null) {
try {
width = Integer.parseInt(widthText);
} catch (final NumberFormatException nfe) {
logger.log(Level.WARNING, "PlatformWindowContextImpl(): Unable to parse window width.", nfe);
}
}
if (heightText != null) {
try {
height = Integer.parseInt(heightText);
} catch (final NumberFormatException nfe) {
logger.log(Level.WARNING, "PlatformWindowContextImpl(): Unable to parse window height.", nfe);
}
}
window.setResizable(isResizable);
window.setSize(width == -1 ? window.getWidth() : width, height == -1 ? window.getHeight() : height);
}
}