Revision: 7163 Author: j...@google.com Date: Tue Nov 24 15:07:18 2009 Log: Merge trunk r7162 into this branch
UI cleanups. svn merge --ignore-ancestry -c7162 \ https://google-web-toolkit.googlecode.com/svn/trunk/ . http://code.google.com/p/google-web-toolkit/source/detail?r=7163 Modified: /releases/2.0/branch-info.txt /releases/2.0/dev/core/src/com/google/gwt/dev/DevModeBase.java /releases/2.0/dev/core/src/com/google/gwt/dev/SwingUI.java /releases/2.0/dev/core/src/com/google/gwt/dev/shell/ShellMainWindow.java /releases/2.0/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java /releases/2.0/dev/core/src/com/google/gwt/dev/shell/remoteui/RemoteUI.java /releases/2.0/dev/core/src/com/google/gwt/dev/ui/DevModeUI.java ======================================= --- /releases/2.0/branch-info.txt Tue Nov 24 14:41:20 2009 +++ /releases/2.0/branch-info.txt Tue Nov 24 15:07:18 2009 @@ -1038,3 +1038,8 @@ Cleans up javadoc in layout panels (TODOs will be captured as issues). Fix for non-standard gwt-Tab style name. svn merge --ignore-ancestry -c7160 https://google-web-toolkit.googlecode.com/svn/trunk/ . + +tr...@7162 was merged into this branch + UI cleanups. + svn merge --ignore-ancestry -c7162 \ + https://google-web-toolkit.googlecode.com/svn/trunk/ . ======================================= --- /releases/2.0/dev/core/src/com/google/gwt/dev/DevModeBase.java Mon Nov 23 21:23:36 2009 +++ /releases/2.0/dev/core/src/com/google/gwt/dev/DevModeBase.java Tue Nov 24 15:07:18 2009 @@ -687,11 +687,11 @@ // Eager AWT init for OS X to ensure safe coexistence with SWT. BootStrapPlatform.initGui(); - if (startUp()) { - // The web server is running now, so launch browsers for startup urls. - launchStartupUrls(); - } - + boolean success = startUp(); + + // The web server is running now, so launch browsers for startup urls. + ui.moduleLoadComplete(success); + blockUntilDone.acquire(); } catch (Exception e) { e.printStackTrace(); @@ -1000,14 +1000,6 @@ return newUI; } - - /** - * Actually launch (or indicate to the user they can be launched) previously - * specified (via {...@link #setStartupUrls(TreeLogger)}) URLs. - */ - private void launchStartupUrls() { - ui.launchStartupUrls(); - } /** * Perform hosted mode relink when new artifacts are generated, without @@ -1030,10 +1022,11 @@ } /** - * Set the set of startup URLs. This is done before launching to allow the - * UI to better present the options to the user, but note that the UI should - * not attempt to launch the URLs until {...@link #launchStartupUrls()} - * is called. + * Set the set of startup URLs. This is done before launching to allow the UI + * to better present the options to the user, but note that the UI should not + * attempt to launch the URLs until + * {...@link DevModeUI#moduleLoadComplete(boolean)} is called, and should not + * automatically launch any URLs if they * * @param logger TreeLogger instance to use */ ======================================= --- /releases/2.0/dev/core/src/com/google/gwt/dev/SwingUI.java Mon Nov 23 19:54:09 2009 +++ /releases/2.0/dev/core/src/com/google/gwt/dev/SwingUI.java Tue Nov 24 15:07:18 2009 @@ -27,6 +27,7 @@ import com.google.gwt.dev.ui.RestartServerEvent; import com.google.gwt.dev.util.collect.HashMap; +import java.awt.EventQueue; import java.awt.Image; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; @@ -37,6 +38,9 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; import javax.swing.Icon; import javax.swing.ImageIcon; @@ -54,22 +58,18 @@ */ protected class SwingModuleHandle implements ModuleHandle { - private final TreeLogger logger; private final ModulePanel tab; /** * Create an immutable module handle. - * - * @param logger logger to use for this module * @param tab the module panel associated with this instance */ - public SwingModuleHandle(TreeLogger logger, ModulePanel tab) { - this.logger = logger; + public SwingModuleHandle(ModulePanel tab) { this.tab = tab; } public TreeLogger getLogger() { - return logger; + return tab.getLogger(); } /** @@ -170,18 +170,24 @@ } @Override - public ModuleHandle getModuleLogger(String userAgent, String remoteSocket, - String url, String tabKey, String moduleName, String sessionKey, - String agentTag, byte[] agentIcon, TreeLogger.Type logLevel) { + public ModuleHandle getModuleLogger(final String userAgent, + final String remoteSocket, final String url, final String tabKey, + final String moduleName, final String sessionKey, final String agentTag, + final byte[] agentIcon, final 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(); + ModuleHandle handle = invokeAndGet(new Callable<ModuleHandle>() { + public ModuleHandle call() throws Exception { + ModuleTabPanel tabPanel = findModuleTab(userAgent, remoteSocket, + url, tabKey, moduleName, agentIcon); + ModulePanel tab = tabPanel.addModuleSession(logLevel, moduleName, + sessionKey, options.getLogFile(String.format("%s-%s-%d.log", + moduleName, agentTag, getNextSessionCounter( + options.getLogDir())))); + // TODO: Switch to a wait cursor? + return new SwingModuleHandle(tab); + } + }); + TreeLogger logger = handle.getLogger(); TreeLogger branch = logger.branch(TreeLogger.INFO, "Loading module " + moduleName); if (url != null) { @@ -195,8 +201,7 @@ if (sessionKey != null) { branch.log(TreeLogger.DEBUG, "Session key: " + sessionKey); } - // TODO: Switch to a wait cursor? - return new SwingModuleHandle(logger, tab); + return handle; } @Override @@ -229,51 +234,63 @@ } @Override - public void initialize(Type logLevel) { + public void initialize(final Type logLevel) { super.initialize(logLevel); - ImageIcon gwtIcon16 = loadImageIcon("icon16.png"); - ImageIcon gwtIcon24 = loadImageIcon("icon24.png"); - ImageIcon gwtIcon32 = loadImageIcon("icon32.png"); - ImageIcon gwtIcon48 = loadImageIcon("icon48.png"); - ImageIcon gwtIcon64 = loadImageIcon("icon64.png"); - ImageIcon gwtIcon128 = loadImageIcon("icon128.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", gwtIcon24, 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(); - } + invokeAndWait(new Runnable() { + public void run() { + ImageIcon gwtIcon16 = loadImageIcon("icon16.png"); + ImageIcon gwtIcon24 = loadImageIcon("icon24.png"); + ImageIcon gwtIcon32 = loadImageIcon("icon32.png"); + ImageIcon gwtIcon48 = loadImageIcon("icon48.png"); + ImageIcon gwtIcon64 = loadImageIcon("icon64.png"); + ImageIcon gwtIcon128 = loadImageIcon("icon128.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", gwtIcon24, 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(); + } + } + }); + setIconImages(topLogger, gwtIcon48, gwtIcon32, gwtIcon64, gwtIcon128, + gwtIcon16); + frame.setVisible(true); } }); - setIconImages(topLogger, gwtIcon48, gwtIcon32, gwtIcon64, gwtIcon128, - gwtIcon16); - frame.setVisible(true); - maybeInitializeOsXApplication(); } @Override - public void launchStartupUrls() { - mainWnd.setLaunchable(); - } - + public void moduleLoadComplete(final boolean success) { + EventQueue.invokeLater(new Runnable() { + public void run() { + mainWnd.moduleLoadComplete(success); + } + }); + } + @Override - public void setStartupUrls(Map<String, URL> urls) { - mainWnd.setStartupUrls(urls); - } - + public void setStartupUrls(final Map<String, URL> urls) { + invokeAndWait(new Runnable() { + public void run() { + mainWnd.setStartupUrls(urls); + } + }); + } + protected int getNextSessionCounter(File logdir) { synchronized (sessionCounterLock) { if (sessionCounter == 0 && logdir != null) { @@ -325,6 +342,45 @@ } return moduleTabPanel; } + + /** + * Invoke a Callable on the UI thread, wait for it to finish, and return the + * result. + * + * @param <T> return type + * @param callable wrapper of the method to run on the UI thread + * @return the return value of callable.call() + * @throws RuntimeException if an error occurs + */ + private <T> T invokeAndGet(Callable<T> callable) { + FutureTask<T> task = new FutureTask<T>(callable); + try { + EventQueue.invokeAndWait(task); + return task.get(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + /** + * Invoke a Runnable on the UI thread and wait for it to finish. + * + * @param runnable + * @throws RuntimeException if an error occurs + */ + private void invokeAndWait(Runnable runnable) { + try { + EventQueue.invokeAndWait(runnable); + } catch (InterruptedException e) { + throw new RuntimeException("Error running on Swing UI thread", e); + } catch (InvocationTargetException e) { + throw new RuntimeException("Error running on Swing UI thread", e); + } + } /** * This method contains code that will call certain Apple extensions to make ======================================= --- /releases/2.0/dev/core/src/com/google/gwt/dev/shell/ShellMainWindow.java Mon Nov 23 19:54:09 2009 +++ /releases/2.0/dev/core/src/com/google/gwt/dev/shell/ShellMainWindow.java Tue Nov 24 15:07:18 2009 @@ -20,7 +20,6 @@ import com.google.gwt.dev.util.BrowserLauncher; import java.awt.BorderLayout; -import java.awt.GridLayout; import java.awt.HeadlessException; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; @@ -46,32 +45,32 @@ public class ShellMainWindow extends JPanel { /** - * A class for implementing different methods of launching a URL. + * Launches a URL by copying it to the clipboard. */ - private abstract static class LaunchMethod { - - private final String displayName; - - /** - * Construct the launch method. - * - * @param displayName the name that will display in the UI for this launch - * method - */ - public LaunchMethod(String displayName) { - this.displayName = displayName; - } - - /** - * Launch the specified URL. - * - * @param url - */ - public abstract void launchUrl(URL url); + private class CopyToClipboardLauncher extends LaunchMethod { + + public CopyToClipboardLauncher() { + super("Copy URL to clipboard"); + } @Override - public String toString() { - return displayName; + public void launchUrl(URL url) { + getLogger().log(TreeLogger.INFO, "Paste " + url.toExternalForm() + + " in a browser"); + // is it better to use SwingUtilities2.canAccessSystemClipboard() here? + Throwable caught = null; + try { + Clipboard clipboard = logWindow.getToolkit().getSystemClipboard(); + StringSelection selection = new StringSelection(url.toExternalForm()); + clipboard.setContents(selection, selection); + return; + } catch (SecurityException e) { + caught = e; + } catch (HeadlessException e) { + caught = e; + } + getLogger().log(TreeLogger.ERROR, "Unable to copy URL to clipboard", + caught); } } @@ -96,36 +95,39 @@ } catch (URISyntaxException e) { caught = e; } - getLogger().log(TreeLogger.ERROR, "Unable to launch default browser", - caught); + TreeLogger branch = getLogger().branch(TreeLogger.ERROR, + "Unable to launch default browser", caught); + branch.log(TreeLogger.INFO, url.toExternalForm()); } } /** - * Launches a URL by copying it to the clipboard. + * A class for implementing different methods of launching a URL. */ - private class CopyToClipboardLauncher extends LaunchMethod { - - public CopyToClipboardLauncher() { - super("Copy URL to clipboard"); - } + private abstract static class LaunchMethod { + + private final String displayName; + + /** + * Construct the launch method. + * + * @param displayName the name that will display in the UI for this launch + * method + */ + public LaunchMethod(String displayName) { + this.displayName = displayName; + } + + /** + * Launch the specified URL. + * + * @param url + */ + public abstract void launchUrl(URL url); @Override - public void launchUrl(URL url) { - // is it better to use SwingUtilities2.canAccessSystemClipboard() here? - Throwable caught = null; - try { - Clipboard clipboard = logWindow.getToolkit().getSystemClipboard(); - StringSelection selection = new StringSelection(url.toExternalForm()); - clipboard.setContents(selection, selection); - return; - } catch (SecurityException e) { - caught = e; - } catch (HeadlessException e) { - caught = e; - } - getLogger().log(TreeLogger.ERROR, "Unable to copy URL to clipboard", - caught); + public String toString() { + return displayName; } } @@ -151,27 +153,22 @@ return urlFragment; } } + private SwingLoggerPanel logWindow; private JComboBox launchCombo; private JButton launchButton; - private JComboBox urlCombo; /** + * Create the main window with the top-level logger and launch controls. + * <p> + * MUST BE CALLED FROM THE UI THREAD + * * @param maxLevel * @param logFile */ public ShellMainWindow(TreeLogger.Type maxLevel, File logFile) { super(new BorderLayout()); - // TODO(jat): add back when we have real options - if (false) { - JPanel panel = new JPanel(new GridLayout(2, 1)); - JPanel optionPanel = new JPanel(); - optionPanel.setBorder(BorderFactory.createTitledBorder("Options")); - optionPanel.add(new JLabel("Miscellaneous options here")); - panel.add(optionPanel); - add(panel, BorderLayout.NORTH); - } JPanel launchPanel = new JPanel(); launchPanel.setBorder(BorderFactory.createTitledBorder( "Launch GWT Module")); @@ -206,10 +203,18 @@ } /** - * Indicate that URLs specified in {...@link #setStartupUrls(Map)} are now - * launchable. + * Indicate that all modules have been loaded -- on success, URLs previously + * specified in {...@link #setStartupUrls(Map)} may be launched. + * <p> + * MUST BE CALLED FROM THE UI THREAD + * + * @param successfulLoad true if all modules were successfully loaded */ - public void setLaunchable() { + public void moduleLoadComplete(boolean successfulLoad) { + if (!successfulLoad) { + launchButton.setText("Module Load Failure"); + return; + } if (urlCombo.getItemCount() == 0) { launchButton.setText("No URLs to Launch"); urlCombo.addItem("No startup URLs"); @@ -222,11 +227,14 @@ /** * Create the UI to show available startup URLs. These should not be - * launchable by the user until the {...@link #setLaunchable()} method is called. - * + * launchable by the user until the {...@link #moduleLoadComplete(boolean)} method is + * called. + * <p> + * MUST BE CALLED FROM THE UI THREAD + * @param urls map of user-specified URL fragments to final URLs */ - public void setStartupUrls(Map<String, URL> urls) { + public void setStartupUrls(final Map<String, URL> urls) { urlCombo.removeAllItems(); ArrayList<String> keys = new ArrayList<String>(urls.keySet()); Collections.sort(keys); @@ -236,15 +244,30 @@ urlCombo.revalidate(); } + /** + * Launch the selected URL with the selected launch method. + * <p> + * MUST BE CALLED FROM THE UI THREAD + */ protected void launch() { LaunchMethod launcher = (LaunchMethod) launchCombo.getSelectedItem(); UrlComboEntry selectedUrl = (UrlComboEntry) urlCombo.getSelectedItem(); + if (launcher == null || selectedUrl == null) { + // Shouldn't happen - should we log anything? + return; + } URL url = selectedUrl.getUrl(); launcher.launchUrl(url); } + /** + * Populate the launch method combo box with possible choices. + * <p> + * MUST BE CALLED FROM THE UI THREAD + */ private void populateLaunchComboBox() { - // TODO(jat): support scanning for other browsers and launching them + // TODO(jat): support scanning for other browsers and launching them, as + // well as user preferences of launch methods and their order. launchCombo.addItem(new DefaultBrowserLauncher()); launchCombo.addItem(new CopyToClipboardLauncher()); } ======================================= --- /releases/2.0/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java Thu Nov 19 14:41:30 2009 +++ /releases/2.0/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java Tue Nov 24 15:07:18 2009 @@ -81,13 +81,12 @@ * We do not want to call the developer's attention to a 404 when * requesting favicon.ico. This is a very common 404. */ - logStatus = normalLogLevel; + logStatus = TreeLogger.TRACE; logHeaders = TreeLogger.DEBUG; } else { logStatus = TreeLogger.WARN; logHeaders = TreeLogger.INFO; } - logHeaders = TreeLogger.INFO; } else if (status >= 400) { logStatus = TreeLogger.WARN; logHeaders = TreeLogger.INFO; ======================================= --- /releases/2.0/dev/core/src/com/google/gwt/dev/shell/remoteui/RemoteUI.java Mon Nov 23 21:23:36 2009 +++ /releases/2.0/dev/core/src/com/google/gwt/dev/shell/remoteui/RemoteUI.java Tue Nov 24 15:07:18 2009 @@ -127,7 +127,7 @@ } @Override - public void launchStartupUrls() { + public void moduleLoadComplete(boolean success) { /* * TODO: Send a message to the server indicating that the URLs are * launchable. ======================================= --- /releases/2.0/dev/core/src/com/google/gwt/dev/ui/DevModeUI.java Mon Nov 23 19:54:09 2009 +++ /releases/2.0/dev/core/src/com/google/gwt/dev/ui/DevModeUI.java Tue Nov 24 15:07:18 2009 @@ -27,6 +27,18 @@ /** * Defines the interaction between DevelopmentMode and the UI, so that * alternate UIs can be implemented. + * <p> + * Sequence of calls: + * <ul> + * <li>{...@link #initialize(Type)} + * <li>{...@link #getTopLogger()} + * <li>possibly {...@link #getWebServerLogger(String, byte[])} + * <li>{...@link #setStartupUrls(Map)} + * <li>{...@link #moduleLoadComplete(boolean)} or + * <li>zero or more {...@link #getModuleLogger} + * </ul> + * {...@link #setCallback(com.google.gwt.dev.ui.UiEvent.Type, UiCallback)} may be + * interspersed among these calls after {...@link #initialize(Type)} is called. */ public abstract class DevModeUI { @@ -111,10 +123,13 @@ } /** - * Indicate to the user that URLs may be started, or actually launch browsers - * with the URLs specified in {...@link #setStartupUrls(Map)}. + * Indicates that all modules have been loaded (loading the XML, not + * completing onModuleLoad), and that URLs previously specified in + * {...@link #setStartupUrls(Map)} may be launched if successful. + * + * @param success true if all modules were successfully loaded */ - public void launchStartupUrls() { + public void moduleLoadComplete(boolean success) { // do nothing by default } -- http://groups.google.com/group/Google-Web-Toolkit-Contributors