blob: e6f3970b95e5959f2a9e04107f563526f64df64b [file] [log] [blame]
/*
* Copyright 2009 Google Inc.
*
* 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 com.google.gwt.dev;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.dev.DevModeBase.HostedModeBaseOptions;
import com.google.gwt.dev.WebServerPanel.RestartAction;
import com.google.gwt.dev.shell.ShellMainWindow;
import com.google.gwt.dev.ui.DevModeUI;
import com.google.gwt.dev.ui.DoneCallback;
import com.google.gwt.dev.ui.DoneEvent;
import com.google.gwt.dev.ui.RestartServerCallback;
import com.google.gwt.dev.ui.RestartServerEvent;
import com.google.gwt.dev.util.collect.HashMap;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.net.URL;
import java.util.Map;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JTabbedPane;
import javax.swing.WindowConstants;
/**
* Implements the Swing UI for development mode.
*/
public class SwingUI extends DevModeUI {
/**
* Module handle for Swing UI.
*/
public class SwingModuleHandle implements ModuleHandle {
private final TreeLogger logger;
private final ModulePanel tab;
public SwingModuleHandle(TreeLogger logger, ModulePanel tab) {
this.logger = logger;
this.tab = tab;
}
public TreeLogger getLogger() {
return logger;
}
public ModulePanel getTab() {
return tab;
}
}
/**
* Interface to group activities related to adding and deleting tabs.
*/
public interface TabPanelCollection {
/**
* Add a new tab containing a ModuleTabPanel.
*
* @param tabPanel
* @param icon
* @param title
* @param tooltip
*/
void addTab(ModuleTabPanel tabPanel, ImageIcon icon, String title,
String tooltip);
/**
* Remove the tab containing a ModuleTabpanel.
*
* @param tabPanel
*/
void removeTab(ModuleTabPanel tabPanel);
}
protected static final String PACKAGE_PATH = SwingUI.class.getPackage(
).getName().replace('.', '/').concat("/shell/");
private static final Object sessionCounterLock = new Object();
private static int sessionCounter = 0;
/**
* Loads an image from the classpath in this package.
*/
static ImageIcon loadImageIcon(String name) {
return loadImageIcon(name, true);
}
/**
* Loads an image from the classpath, optionally prepending this package.
*
* @param name name of an image file.
* @param prependPackage true if {@link #PACKAGE_PATH} should be prepended to
* this name.
* @return an ImageIcon instance to use -- in the event of an error loading
* the requested image, a blank ImageIcon is returned
*/
static ImageIcon loadImageIcon(String name, boolean prependPackage) {
ClassLoader cl = SwingUI.class.getClassLoader();
if (prependPackage) {
name = PACKAGE_PATH + name;
}
URL url = (name == null) ? null : cl.getResource(name);
if (url != null) {
ImageIcon image = new ImageIcon(url);
return image;
} else {
// Bad image.
return new ImageIcon();
}
}
private final HostedModeBaseOptions options;
private final Map<DevelModeTabKey, ModuleTabPanel> tabPanels = new HashMap<
DevelModeTabKey, ModuleTabPanel>();
private ShellMainWindow mainWnd;
private JFrame frame;
private JTabbedPane tabs;
private WebServerPanel webServerLog;
private TreeLogger topLogger;
/**
* Create a Swing UI instance.
*
* @param options parsed command-line options
*/
public SwingUI(HostedModeBaseOptions options) {
this.options = options;
}
@Override
public TreeLogger getTopLogger() {
return topLogger;
}
@Override
public TreeLogger getWebServerLogger(String serverName, byte[] serverIcon) {
if (webServerLog == null) {
RestartAction restartAction = null;
final RestartServerCallback callback = getCallback(
RestartServerEvent.getType());
if (callback != null) {
restartAction = new RestartAction() {
public void restartServer(TreeLogger logger) {
callback.onRestartServer(logger);
}
};
}
webServerLog = new WebServerPanel(options.getPort(), getLogLevel(),
options.getLogFile("webserver.log"), restartAction);
Icon serverIconImage = null;
if (serverIcon != null) {
serverIconImage = new ImageIcon(serverIcon);
}
tabs.insertTab(serverName, serverIconImage, webServerLog, null, 1);
}
return webServerLog.getLogger();
}
@Override
public void initialize(Type logLevel) {
super.initialize(logLevel);
ImageIcon gwtIcon = loadImageIcon("icon24.png");
frame = new JFrame("GWT Development Mode");
tabs = new JTabbedPane();
if (options.alsoLogToFile()) {
options.getLogDir().mkdirs();
}
mainWnd = new ShellMainWindow(logLevel, options.getLogFile("main.log"));
topLogger = mainWnd.getLogger();
tabs.addTab("Development Mode", gwtIcon, mainWnd, "GWT Development Mode");
frame.getContentPane().add(tabs);
frame.setSize(950, 700);
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
DoneCallback callback = getCallback(DoneEvent.getType());
if (callback != null) {
callback.onDone();
}
}
});
frame.setIconImage(loadImageIcon("icon16.png").getImage());
frame.setVisible(true);
}
@Override
public ModuleHandle loadModule(String userAgent, String remoteSocket,
String url, String tabKey, String moduleName, String sessionKey,
String agentTag, byte[] agentIcon, TreeLogger.Type logLevel) {
// TODO(jat): add support for closing an active module
ModuleTabPanel tabPanel = null;
ModulePanel tab = null;
tabPanel = findModuleTab(userAgent, remoteSocket, url, tabKey,
moduleName, agentIcon);
tab = tabPanel.addModuleSession(logLevel, moduleName, sessionKey,
options.getLogFile(String.format("%s-%s-%d.log", moduleName, agentTag,
getNextSessionCounter(options.getLogDir()))));
TreeLogger logger = tab.getLogger();
TreeLogger branch = logger.branch(TreeLogger.INFO, "Loading module "
+ moduleName);
if (url != null) {
branch.log(TreeLogger.INFO, "Top URL: " + url);
}
branch.log(TreeLogger.INFO, "User agent: " + userAgent);
branch.log(TreeLogger.TRACE, "Remote socket: " + remoteSocket);
if (tabKey != null) {
branch.log(TreeLogger.DEBUG, "Tab key: " + tabKey);
}
if (sessionKey != null) {
branch.log(TreeLogger.DEBUG, "Session key: " + sessionKey);
}
// TODO: Switch to a wait cursor?
return new SwingModuleHandle(logger, tab);
}
@Override
public void unloadModule(ModuleHandle module) {
SwingModuleHandle handle = (SwingModuleHandle) module;
Disconnectable tab = handle.getTab();
if (tab != null) {
tab.disconnect();
}
}
protected int getNextSessionCounter(File logdir) {
synchronized (sessionCounterLock) {
if (sessionCounter == 0 && logdir != null) {
// first time only, figure out the "last" session count already in use
for (String filename : logdir.list()) {
if (filename.matches("^[A-Za-z0-9_$]*-[a-z]*-[0-9]*.log$")) {
String substring = filename.substring(filename.lastIndexOf('-') + 1,
filename.length() - 4);
int number = Integer.parseInt(substring);
if (number > sessionCounter) {
sessionCounter = number;
}
}
}
}
//
return ++sessionCounter;
}
}
private ModuleTabPanel findModuleTab(String userAgent, String remoteSocket,
String url, String tabKey, String moduleName, byte[] agentIcon) {
int hostEnd = remoteSocket.indexOf(':');
if (hostEnd < 0) {
hostEnd = remoteSocket.length();
}
String remoteHost = remoteSocket.substring(0, hostEnd);
final DevelModeTabKey key = new DevelModeTabKey(userAgent, url, tabKey,
remoteHost);
ModuleTabPanel moduleTabPanel = tabPanels.get(key);
if (moduleTabPanel == null) {
moduleTabPanel = new ModuleTabPanel(userAgent, remoteSocket, url,
agentIcon, new TabPanelCollection() {
public void addTab(ModuleTabPanel tabPanel, ImageIcon icon,
String title, String tooltip) {
synchronized (tabs) {
tabs.addTab(title, icon, tabPanel, tooltip);
tabPanels.put(key, tabPanel);
}
}
public void removeTab(ModuleTabPanel tabPanel) {
synchronized (tabs) {
tabs.remove(tabPanel);
tabPanels.remove(key);
}
}
}, moduleName);
}
return moduleTabPanel;
}
}