org.vectomatic.svg.edit.client.SvgrealApp.java Source code

Java tutorial

Introduction

Here is the source code for org.vectomatic.svg.edit.client.SvgrealApp.java

Source

/**********************************************
 * Copyright (C) 2010 Lukas Laag
 * This file is part of svgreal.
 * 
 * svgreal 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 version 3 of the License, or
 * (at your option) any later version.
 * 
 * svgreal 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 svgreal.  If not, see http://www.gnu.org/licenses/
 **********************************************/
package org.vectomatic.svg.edit.client;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.vectomatic.dom.svg.OMSVGDocument;
import org.vectomatic.dom.svg.OMSVGSVGElement;
import org.vectomatic.dom.svg.impl.SVGElement;
import org.vectomatic.dom.svg.impl.SVGSVGElement;
import org.vectomatic.dom.svg.ui.SVGResource;
import org.vectomatic.dom.svg.utils.OMSVGParser;
import org.vectomatic.dom.svg.utils.SVGConstants;
import org.vectomatic.file.File;
import org.vectomatic.file.FileList;
import org.vectomatic.file.FileUploadExt;
import org.vectomatic.svg.edit.client.command.CommandFactories;
import org.vectomatic.svg.edit.client.command.CommandFactorySelector;
import org.vectomatic.svg.edit.client.engine.SVGModel;
import org.vectomatic.svg.edit.client.engine.SVGProcessor;
import org.vectomatic.svg.edit.client.event.ActivateWindowEvent;
import org.vectomatic.svg.edit.client.event.ActivateWindowHandler;
import org.vectomatic.svg.edit.client.event.DeactivateWindowEvent;
import org.vectomatic.svg.edit.client.event.DeactivateWindowHandler;
import org.vectomatic.svg.edit.client.event.HasActivateWindowHandlers;
import org.vectomatic.svg.edit.client.event.HasDeactivateWindowHandlers;
import org.vectomatic.svg.edit.client.gxt.panels.CommandFactoryToolBar;
import org.vectomatic.svg.edit.client.gxt.widget.RecentDocMenuItem;
import org.vectomatic.svg.edit.client.gxt.widget.ViewportExt;
import org.vectomatic.svg.edit.client.inspector.InspectorWindow;
import org.vectomatic.svg.edit.client.load.FileLoadRequest;
import org.vectomatic.svg.edit.client.load.ILoadRequest;
import org.vectomatic.svg.edit.client.load.InternalLoadRequest;
import org.vectomatic.svg.edit.client.load.NewDocRequest;
import org.vectomatic.svg.edit.client.load.RSSReader;
import org.vectomatic.svg.edit.client.load.UrlLoadRequest;
import org.vectomatic.svg.edit.client.model.MetaModel;
import org.vectomatic.svg.edit.client.model.svg.CssContextModel;
import org.vectomatic.svg.edit.client.model.svg.SVGCircleElementModel;
import org.vectomatic.svg.edit.client.model.svg.SVGEllipseElementModel;
import org.vectomatic.svg.edit.client.model.svg.SVGImageElementModel;
import org.vectomatic.svg.edit.client.model.svg.SVGLineElementModel;
import org.vectomatic.svg.edit.client.model.svg.SVGNamedElementModel;
import org.vectomatic.svg.edit.client.model.svg.SVGPolygonElementModel;
import org.vectomatic.svg.edit.client.model.svg.SVGPolylineElementModel;
import org.vectomatic.svg.edit.client.model.svg.SVGRectElementModel;
import org.vectomatic.svg.edit.client.utils.DecoratedImageCache;
import org.vectomatic.svg.edit.client.utils.ImageLoader;

import com.extjs.gxt.ui.client.Style.LayoutRegion;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.event.MenuEvent;
import com.extjs.gxt.ui.client.event.MessageBoxEvent;
import com.extjs.gxt.ui.client.event.SelectionListener;
import com.extjs.gxt.ui.client.event.WindowEvent;
import com.extjs.gxt.ui.client.util.Rectangle;
import com.extjs.gxt.ui.client.util.Size;
import com.extjs.gxt.ui.client.widget.Component;
import com.extjs.gxt.ui.client.widget.ContentPanel;
import com.extjs.gxt.ui.client.widget.Info;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
import com.extjs.gxt.ui.client.widget.MessageBox;
import com.extjs.gxt.ui.client.widget.Window;
import com.extjs.gxt.ui.client.widget.button.Button;
import com.extjs.gxt.ui.client.widget.layout.BorderLayout;
import com.extjs.gxt.ui.client.widget.layout.BorderLayoutData;
import com.extjs.gxt.ui.client.widget.layout.HBoxLayout;
import com.extjs.gxt.ui.client.widget.layout.HBoxLayout.HBoxLayoutAlign;
import com.extjs.gxt.ui.client.widget.layout.HBoxLayoutData;
import com.extjs.gxt.ui.client.widget.menu.Menu;
import com.extjs.gxt.ui.client.widget.menu.MenuBar;
import com.extjs.gxt.ui.client.widget.menu.MenuBarItem;
import com.extjs.gxt.ui.client.widget.menu.MenuItem;
import com.extjs.gxt.ui.client.widget.menu.SeparatorMenuItem;
import com.extjs.gxt.ui.client.widget.toolbar.ToolBar;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.dom.client.Style.Visibility;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.logical.shared.InitializeEvent;
import com.google.gwt.event.logical.shared.InitializeHandler;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.event.shared.SimpleEventBus;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Window.Location;
import com.google.gwt.user.client.ui.AbstractImagePrototype;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.RootPanel;

/**
 * Application main class.
 * @author laaglu
 */
public class SvgrealApp implements EntryPoint, HasActivateWindowHandlers, HasDeactivateWindowHandlers {
    /**
     * The locale URL parameter
     */
    private static final String PARAM_LOCALE = "locale";
    /**
     * The gwt.codesvr URL parameter
     */
    private static final String PARAM_CODE_SERVER = "gwt.codesvr";
    /**
     * The demo URL parameter
     */
    private static final String PARAM_DEMO = "demo";
    /**
     * Application singleton
     */
    private static SvgrealApp instance;
    /**
     * Application-wide event bus
     */
    private EventBus eventBus;
    /**
     * List of open documents
     */
    private List<SVGWindow> svgWindows;
    /**
     * Currently active document
     */
    private SVGWindow activeWindow;
    /**
     * Open URL panel
     */
    private MessageBox openUrlBox;
    /**
     * OpenClipArt RSS feed reader
     */
    private RSSReader rssReader;
    /**
     * About panel
     */
    private AboutDialog aboutDialog;
    /**
     * Inspector
     */
    private InspectorWindow inspectorWindow;
    /**
     * The command factory selector
     */
    private CommandFactorySelector commandFactorySelector;
    /**
     * The command toolbar at the bottom of the screen
     */
    private CommandFactoryToolBar commandToolBar;
    /**
     * To process local file open requests
     */
    FileUploadExt fileUpload;
    /**
     * CSS context model to provide default values
     * when creating new SVG elements
     */
    private CssContextModel cssContext;
    /**
     * A cache for decorated treeview icons
     */
    private DecoratedImageCache imageCache;

    private List<MetaModel<?>> metaModels;
    private int windowX, windowY;
    private ViewportExt viewport;
    private Menu recentDocsMenu;
    private MenuItem resetViewItem;
    private MenuItem tileWindowsItem;
    private MenuItem stackWindowsItem;
    private MenuItem closeWindowItem;
    private MenuItem exportAsSvgMenuItem;
    private Menu displayWindowMenu;
    private MenuItem displayWindowMenuItem;
    private Map<String, ImageResource> localeToIcon;
    /**
     * To process menu item selection events
     */
    private SelectionListener<MenuEvent> dispatcher;
    private Menu languageMenu;

    public SvgrealApp() {

    }

    public void onModuleLoad() {
        instance = this;
        GWT.setUncaughtExceptionHandler(new GWT.UncaughtExceptionHandler() {
            public void onUncaughtException(Throwable throwable) {
                GWT.log("Uncaught exception", throwable);
                if (!GWT.isScript()) {
                    String text = "Uncaught exception: ";
                    while (throwable != null) {
                        StackTraceElement[] stackTraceElements = throwable.getStackTrace();
                        text += throwable.toString() + "\n";
                        for (int i = 0; i < stackTraceElements.length; i++) {
                            text += "    at " + stackTraceElements[i] + "\n";
                        }
                        throwable = throwable.getCause();
                        if (throwable != null) {
                            text += "Caused by: ";
                        }
                    }
                    DialogBox dialogBox = new DialogBox(true);
                    DOM.setStyleAttribute(dialogBox.getElement(), "backgroundColor", "#ABCDEF");
                    System.err.print(text);
                    text = text.replaceAll(" ", "&nbsp;");
                    dialogBox.setHTML("<pre>" + text + "</pre>");
                    dialogBox.center();
                }
            }
        });
        AppBundle.INSTANCE.css().ensureInjected();

        // Create graphical context
        OMSVGDocument doc = OMSVGParser.currentDocument();
        SVGElement element = doc.createSVGPathElement().getElement().cast();
        element.getStyle().setProperty(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_LIGHTBLUE_VALUE);
        element.getStyle().setProperty(SVGConstants.CSS_STROKE_PROPERTY, SVGConstants.CSS_BLACK_VALUE);
        SVGNamedElementModel.createTitleDesc(element, AppConstants.INSTANCE.graphicalContext());
        cssContext = new CssContextModel(element);

        svgWindows = new ArrayList<SVGWindow>();
        viewport = new ViewportExt();

        viewport.setLayout(new BorderLayout());
        HBoxLayout hboxLayout = new HBoxLayout();
        hboxLayout.setHBoxLayoutAlign(HBoxLayoutAlign.TOP);
        LayoutContainer menuBarContainer = new LayoutContainer(hboxLayout);
        HBoxLayoutData layoutData = new HBoxLayoutData();
        layoutData.setFlex(1);
        menuBarContainer.add(createMenuBar(), layoutData);
        menuBarContainer.add(createLanguageBar(), new HBoxLayoutData());
        viewport.add(menuBarContainer, new BorderLayoutData(LayoutRegion.NORTH, getWindowBarHeight()));
        viewport.setStyleAttribute("background-color", SVGConstants.CSS_BEIGE_VALUE);

        commandToolBar = new CommandFactoryToolBar(CommandFactories.getAllFactoriesStore(),
                getCommandFactorySelector());
        ContentPanel commandPanel = new ContentPanel();
        commandPanel.setHeaderVisible(false);
        commandPanel.setBottomComponent(commandToolBar);
        viewport.add(commandPanel, new BorderLayoutData(LayoutRegion.SOUTH, getWindowBarHeight()));

        update();

        fileUpload = new FileUploadExt();
        Style style = fileUpload.getElement().getStyle();
        style.setVisibility(Visibility.HIDDEN);
        style.setWidth(0, Unit.PX);
        style.setHeight(0, Unit.PX);
        fileUpload.addChangeHandler(new ChangeHandler() {

            @Override
            public void onChange(ChangeEvent event) {
                processFiles(fileUpload.getFiles());
            }
        });

        RootPanel.get().add(viewport);
        RootPanel.get().add(fileUpload);

        ImageLoader loader = new ImageLoader();
        loader.addInitializeHandler(new InitializeHandler() {

            @Override
            public void onInitialize(InitializeEvent event) {
                ImageLoader loader = (ImageLoader) event.getSource();
                imageCache = new DecoratedImageCache(loader.getImages());
                init();
            }
        });
        loader.loadImages(new ImageResource[] { AppBundle.INSTANCE.altGlyphDef(), AppBundle.INSTANCE.altGlyphItem(),
                AppBundle.INSTANCE.altGlyph(), AppBundle.INSTANCE.animateColor(),
                AppBundle.INSTANCE.animateMotion(), AppBundle.INSTANCE.animate(),
                AppBundle.INSTANCE.animateTransform(), AppBundle.INSTANCE.circle(), AppBundle.INSTANCE.clipPath(),
                AppBundle.INSTANCE.colorProfile(), AppBundle.INSTANCE.cursor(), AppBundle.INSTANCE.defs(),
                AppBundle.INSTANCE.desc(), AppBundle.INSTANCE.ellipse(), AppBundle.INSTANCE.feBlend(),
                AppBundle.INSTANCE.feColorMatrix(), AppBundle.INSTANCE.feComponentTransfer(),
                AppBundle.INSTANCE.feComposite(), AppBundle.INSTANCE.feConvolveMatrix(),
                AppBundle.INSTANCE.feDiffuseLighting(), AppBundle.INSTANCE.feDisplacementMap(),
                AppBundle.INSTANCE.feDistantLight(), AppBundle.INSTANCE.feFlood(), AppBundle.INSTANCE.feFuncA(),
                AppBundle.INSTANCE.feFuncB(), AppBundle.INSTANCE.feFuncG(), AppBundle.INSTANCE.feFuncR(),
                AppBundle.INSTANCE.feGaussianBlur(), AppBundle.INSTANCE.feMergeNode(), AppBundle.INSTANCE.feMerge(),
                AppBundle.INSTANCE.feMorphology(), AppBundle.INSTANCE.feOffset(), AppBundle.INSTANCE.fePointLight(),
                AppBundle.INSTANCE.feSpecularLight(), AppBundle.INSTANCE.feSpotLight(), AppBundle.INSTANCE.feTile(),
                AppBundle.INSTANCE.feTurbulence(), AppBundle.INSTANCE.filter(), AppBundle.INSTANCE.fontFaceFormat(),
                AppBundle.INSTANCE.fontFaceName(), AppBundle.INSTANCE.fontFace(), AppBundle.INSTANCE.fontFaceSrc(),
                AppBundle.INSTANCE.fontFaceUri(), AppBundle.INSTANCE.font(), AppBundle.INSTANCE.foreignObject(),
                AppBundle.INSTANCE.glyph(), AppBundle.INSTANCE.glyphRef(), AppBundle.INSTANCE.g(),
                AppBundle.INSTANCE.hkern(), AppBundle.INSTANCE.image(), AppBundle.INSTANCE.linearGradient(),
                AppBundle.INSTANCE.line(), AppBundle.INSTANCE.marker(), AppBundle.INSTANCE.mask(),
                AppBundle.INSTANCE.metadata(), AppBundle.INSTANCE.missingGlyph(), AppBundle.INSTANCE.mpath(),
                AppBundle.INSTANCE.path(), AppBundle.INSTANCE.pattern(), AppBundle.INSTANCE.polygon(),
                AppBundle.INSTANCE.polyline(), AppBundle.INSTANCE.radialGradient(), AppBundle.INSTANCE.rect(),
                AppBundle.INSTANCE.script(), AppBundle.INSTANCE.set(), AppBundle.INSTANCE.stop(),
                AppBundle.INSTANCE.style(), AppBundle.INSTANCE.svg(), AppBundle.INSTANCE.switch_(),
                AppBundle.INSTANCE.symbol(), AppBundle.INSTANCE.textPath(), AppBundle.INSTANCE.text(),
                AppBundle.INSTANCE.title(), AppBundle.INSTANCE.tref(), AppBundle.INSTANCE.tspan(),
                AppBundle.INSTANCE.use(), AppBundle.INSTANCE.view(), AppBundle.INSTANCE.vkern(),
                AppBundle.INSTANCE.error(), AppBundle.INSTANCE.warning(), });
    }

    private void init() {
        // Open demo documents if requested by the demo URL parameter
        String demo = getParameter(PARAM_DEMO);
        if (demo != null) {
            SVGResource[] demoResources = { AppBundle.INSTANCE.fish(), AppBundle.INSTANCE.fries(),
                    AppBundle.INSTANCE.chess(), AppBundle.INSTANCE.sample() };
            String[] demoNames = { "fish.svg", "fries.svg", "chess.svg", "sample.svg" };
            for (int i = 0; i < demoResources.length; i++) {
                if (demo.indexOf(Integer.toString(i)) != -1) {
                    new InternalLoadRequest(demoResources[i], demoNames[i]).load();
                }
            }
        }
    }

    private MenuBar createMenuBar() {
        Menu fileMenu = new Menu();
        AppConstants cst = AppConstants.INSTANCE;
        final MenuItem newDocumentMenuItem = new MenuItem(cst.newDocumentMenuItem());
        fileMenu.add(newDocumentMenuItem);
        final MenuItem openUrlItem = new MenuItem(cst.openUrlMenuItem());
        fileMenu.add(openUrlItem);
        final MenuItem openLocalMenuItem = new MenuItem(cst.openLocalMenuItem());
        fileMenu.add(openLocalMenuItem);
        final MenuItem openRssFeedItem = new MenuItem(cst.openRssFeedMenuItem());
        fileMenu.add(openRssFeedItem);
        exportAsSvgMenuItem = new MenuItem(cst.exportAsSvgMenuItem());
        fileMenu.add(exportAsSvgMenuItem);
        MenuItem recentDocumentsItem = new MenuItem(cst.recentDocumentsMenuItem());
        recentDocsMenu = new Menu();
        recentDocumentsItem.setSubMenu(recentDocsMenu);
        fileMenu.add(recentDocumentsItem);

        Menu windowMenu = new Menu();
        resetViewItem = new MenuItem(cst.resetViewMenuItem());
        windowMenu.add(resetViewItem);
        windowMenu.add(new SeparatorMenuItem());
        tileWindowsItem = new MenuItem(cst.tileWindowsMenuItem());
        windowMenu.add(tileWindowsItem);
        stackWindowsItem = new MenuItem(cst.stackWindowsMenuItem());
        windowMenu.add(stackWindowsItem);
        displayWindowMenuItem = new MenuItem(cst.displayWindowMenuItem());
        displayWindowMenu = new Menu();
        displayWindowMenuItem.setSubMenu(displayWindowMenu);
        windowMenu.add(displayWindowMenuItem);
        windowMenu.add(new SeparatorMenuItem());
        closeWindowItem = new MenuItem(cst.closeWindowMenuItem());
        windowMenu.add(closeWindowItem);

        Menu toolsMenu = new Menu();
        final MenuItem inspectorMenuItem = new MenuItem(cst.inspectorMenuItem());
        toolsMenu.add(inspectorMenuItem);

        Menu aboutMenu = new Menu();
        final MenuItem aboutItem = new MenuItem(cst.aboutMenuItem());
        aboutMenu.add(aboutItem);

        MenuBar menuBar = new MenuBar();
        menuBar.setBorders(true);
        menuBar.setStyleAttribute("borderTop", "none");
        menuBar.add(new MenuBarItem(cst.fileMenu(), fileMenu));
        menuBar.add(new MenuBarItem(cst.windowMenu(), windowMenu));
        menuBar.add(new MenuBarItem(cst.toolsMenu(), toolsMenu));
        menuBar.add(new MenuBarItem(cst.aboutMenu(), aboutMenu));

        dispatcher = new SelectionListener<MenuEvent>() {
            @Override
            public void componentSelected(MenuEvent me) {
                MenuItem item = (MenuItem) me.getItem();
                if (item == newDocumentMenuItem) {
                    newDocument();
                } else if (item == openUrlItem) {
                    openUrl();
                } else if (item == openLocalMenuItem) {
                    openLocal();
                } else if (item == openRssFeedItem) {
                    openRssFeed();
                } else if (item == exportAsSvgMenuItem) {
                    exportAsSvg();
                } else if (item == resetViewItem) {
                    resetView();
                } else if (item == tileWindowsItem) {
                    tileWindows();
                } else if (item == stackWindowsItem) {
                    stackWindows();
                } else if (item == closeWindowItem) {
                    closeWindow(activeWindow);
                } else if (item == inspectorMenuItem) {
                    inspector();
                } else if (item == aboutItem) {
                    about();
                } else if (item.getParentMenu() == displayWindowMenu) {
                    for (SVGWindow window : svgWindows) {
                        if (window.getHeading().equals(item.getText())) {
                            window.setActive(true);
                            window.toFront();
                            break;
                        }
                    }
                } else if (item.getParentMenu() == languageMenu) {
                    redirect(item.getText());
                }
            }
        };
        newDocumentMenuItem.addSelectionListener(dispatcher);
        openUrlItem.addSelectionListener(dispatcher);
        openLocalMenuItem.addSelectionListener(dispatcher);
        openRssFeedItem.addSelectionListener(dispatcher);
        exportAsSvgMenuItem.addSelectionListener(dispatcher);
        resetViewItem.addSelectionListener(dispatcher);
        tileWindowsItem.addSelectionListener(dispatcher);
        stackWindowsItem.addSelectionListener(dispatcher);
        closeWindowItem.addSelectionListener(dispatcher);
        inspectorMenuItem.addSelectionListener(dispatcher);
        aboutItem.addSelectionListener(dispatcher);
        return menuBar;
    }

    public void redirect(String language) {
        StringBuilder path = new StringBuilder(GWT.getHostPageBaseURL());
        path.append("?");
        path.append(PARAM_LOCALE);
        path.append("=");
        path.append(language);
        path.append(copyParam(PARAM_CODE_SERVER));
        path.append(copyParam(PARAM_DEMO));
        Location.replace(path.toString());
    }

    private static String copyParam(String name) {
        StringBuilder copy = new StringBuilder();
        String value = getParameter(name);
        if (value != null && value.length() > 0) {
            copy.append("&");
            copy.append(name);
            copy.append("=");
            copy.append(value);
        }
        return copy.toString();
    }

    private ToolBar createLanguageBar() {
        if (localeToIcon == null) {
            localeToIcon = new HashMap<String, ImageResource>();
            localeToIcon.put("fr", AppBundle.INSTANCE.flagFr());
            localeToIcon.put("en", AppBundle.INSTANCE.flagUs());
        }
        ToolBar toolBar = new ToolBar();
        languageMenu = new Menu();
        for (Map.Entry<String, ImageResource> entry : localeToIcon.entrySet()) {
            MenuItem item = new MenuItem(entry.getKey(), AbstractImagePrototype.create(entry.getValue()));
            item.addSelectionListener(dispatcher);
            languageMenu.add(item);
        }
        String locale = getParameter(PARAM_LOCALE);
        if (!localeToIcon.containsKey(locale)) {
            locale = "en";
        }
        Button languageButton = new Button(locale, AbstractImagePrototype.create((localeToIcon.get(locale))));
        languageButton.setMenu(languageMenu);
        toolBar.add(languageButton);
        toolBar.setWidth(60);
        toolBar.setHeight(getWindowBarHeight() - 1);
        return toolBar;
    }

    public SVGWindow addWindow(OMSVGSVGElement svg, ILoadRequest request) {
        String title = request.getTitle();
        SVGModel model = SVGModel.newInstance(svg, title, SVGProcessor.newIdPrefix());
        SVGWindow window = new SVGWindow(model);
        window.setHeading(title);
        svgWindows.add(window);
        MenuItem displayItem = new MenuItem(title);
        displayItem.addSelectionListener(dispatcher);
        displayWindowMenu.add(displayItem);

        // To be notified when a window is activated in order to
        // keep track of the active window 
        window.addListener(Events.Activate, new Listener<WindowEvent>() {

            @Override
            public void handleEvent(WindowEvent we) {
                SVGWindow svgWindow = (SVGWindow) we.getWindow();
                GWT.log("VectomaticApp2.Activate(" + svgWindow.getHeading() + ")");
                if (activeWindow != svgWindow) {
                    if (activeWindow != null) {
                        activeWindow.deactivate();
                        fireEvent(new DeactivateWindowEvent(activeWindow));
                    }
                    activeWindow = svgWindow;
                    activeWindow.activate();
                    fireEvent(new ActivateWindowEvent(activeWindow));
                }
            }
        });
        window.addListener(Events.BeforeHide, new Listener<WindowEvent>() {

            @Override
            public void handleEvent(WindowEvent we) {
                SVGWindow svgWindow = (SVGWindow) we.getWindow();
                svgWindow.getSvgModel().getSelectionModel().deselectAll();
                GWT.log("VectomaticApp2.BeforeHide(" + svgWindow.getHeading() + ")");
                svgWindow.removeAllListeners();
                svgWindow.deactivate();
                fireEvent(new DeactivateWindowEvent(svgWindow));
                for (Component c : displayWindowMenu.getItems()) {
                    MenuItem item = (MenuItem) c;
                    if (item.getText().equals(svgWindow.getHeading())) {
                        displayWindowMenu.remove(item);
                        break;
                    }
                }
                svgWindows.remove(svgWindow);
                activeWindow = null;
                update();
            }
        });

        // Update the recent docs menu
        if (!(request instanceof NewDocRequest)) {
            List<Component> recentDocs = recentDocsMenu.getItems();
            boolean alreadyInRecentDocs = false;
            for (Component item : recentDocs) {
                RecentDocMenuItem menuItem = (RecentDocMenuItem) item;
                if (request.equals(menuItem.getRequest())) {
                    alreadyInRecentDocs = true;
                    break;
                }
            }
            if (!alreadyInRecentDocs) {
                if (recentDocs.size() >= 8) {
                    Component oldestItem = recentDocs.get(0);
                    oldestItem.removeAllListeners();
                    recentDocsMenu.remove(oldestItem);
                }
                RecentDocMenuItem recentDocItem = new RecentDocMenuItem(request);
                recentDocsMenu.add(recentDocItem);
            }
        }

        int windowBarHeight = getWindowBarHeight();
        windowY += windowBarHeight;
        window.setPagePosition(windowX, windowY);
        windowX += windowBarHeight;
        //      window.show();
        viewport.add(window);
        window.setVisible(true);
        update();
        return window;
    }

    public void newDocument() {
        GWT.log("newDocument()");
        new NewDocRequest().load();
    }

    public void openUrl() {
        GWT.log("openUrl()");
        openUrlBox = MessageBox.prompt(AppConstants.INSTANCE.openUrlMenuItem(),
                AppConstants.INSTANCE.openUrlText());
        openUrlBox.addCallback(new Listener<MessageBoxEvent>() {
            public void handleEvent(MessageBoxEvent be) {
                new UrlLoadRequest(be.getValue()).load();
            }
        });
    }

    public void info(String command, String message) {
        Info.display(command, message);
    }

    public void openRssFeed() {
        GWT.log("openRssFeed()");
        if (rssReader == null) {
            rssReader = new RSSReader();
        }
        rssReader.show();
    }

    private void openLocal() {
        GWT.log("openLocal()");
        fileUpload.click();
    }

    private void processFiles(FileList files) {
        for (File file : files) {
            String type = file.getType();
            if ("image/svg+xml".equals(type)) {
                new FileLoadRequest(file).load();
            }
        }
    }

    protected void exportAsSvg() {
        GWT.log("exportAsSvg()");
        SVGModel model = getActiveModel();
        String markup = model.getMarkup();
        String url = "data:image/svg+xml;base64," + base64encode(markup);
        String title = ((SVGNamedElementModel) model.getRoot()).getName();
        com.google.gwt.user.client.Window.open(url, title, "");
    }

    private static native String base64encode(String str) /*-{
                                                          return $wnd.btoa(str);
                                                          }-*/;

    public void resetView() {
        GWT.log("resetView()");
        activeWindow.setRotationCompass(0);
        activeWindow.setScaleSlider(50);
    }

    private List<Window> getAllWindows() {
        List<Window> windows = new ArrayList<Window>(svgWindows);
        if (inspectorWindow != null && inspectorWindow.isVisible()) {
            windows.add(inspectorWindow);
        }
        return windows;
    }

    public void tileWindows() {
        GWT.log("tileWindows()");
        List<Window> windows = getAllWindows();
        Rectangle rect = getRectangle();
        int count = windows.size();
        int cols = (int) Math.ceil(Math.sqrt(count));
        int rows = (int) Math.ceil((double) count / cols);
        GWT.log("cols=" + cols + "; rows=" + rows);
        Size windowSize = new Size(rect.width / cols, rect.height / rows);
        int index = 0;
        for (Window window : windows) {
            window.setSize(windowSize.width, windowSize.height);
            int x = index % cols;
            int y = index / cols;
            window.setPagePosition(rect.x + x * windowSize.width, rect.y + y * windowSize.height);
            index++;
        }
    }

    public void stackWindows() {
        GWT.log("stackWindows()");
        List<Window> windows = getAllWindows();
        Rectangle rect = getRectangle();
        Size size = viewport.getSize();
        Size windowSize = new Size((int) (size.width * 0.75f), (int) (size.height * 0.75f));
        int windowBarHeight = getWindowBarHeight();
        int index = 0;
        for (Window window : windows) {
            window.setSize(windowSize.width, windowSize.height);
            int x = rect.x + index * windowBarHeight;
            int y = rect.y + index * windowBarHeight;
            window.setPagePosition(x, y);
            index++;
            if (y + windowSize.height > size.height) {
                x = rect.x;
                y = rect.y;
                index = 0;
            }
        }
    }

    public void closeWindow(SVGWindow window) {
        GWT.log("closeWindow()");
        if (window != null) {
            window.hide();
        }
    }

    public static String getParameter(String param) {
        return com.google.gwt.user.client.Window.Location.getParameter(param);
    }

    public SVGWindow getActiveWindow() {
        return activeWindow;
    }

    public SVGModel getActiveModel() {
        if (activeWindow != null) {
            return activeWindow.getSvgModel();
        }
        return null;
    }

    public SVGWindow getWindow(SVGElement element) {
        SVGSVGElement svg = element.getOwnerSVGElement();
        for (SVGWindow svgWindow : svgWindows) {
            if (svg == svgWindow.getSvgModel().getDocumentRoot().getElement()) {
                return svgWindow;
            }
        }
        return null;
    }

    public void about() {
        GWT.log("about()");
        if (aboutDialog == null) {
            aboutDialog = new AboutDialog();
        }
        aboutDialog.show();
    }

    public Rectangle getRectangle() {
        int windowBarHeight = getWindowBarHeight();
        Size commandBarSize = commandToolBar.getSize();
        Size viewPortSize = viewport.getSize();
        Rectangle rect = new Rectangle(0, windowBarHeight, viewPortSize.width,
                viewPortSize.height - windowBarHeight - commandBarSize.height);
        return rect;
    }

    public static final int getWindowBarHeight() {
        return 27;
    }

    private void update() {
        boolean hasActiveWindow = svgWindows.size() > 0;
        exportAsSvgMenuItem.setEnabled(hasActiveWindow);
        closeWindowItem.setEnabled(hasActiveWindow);
        tileWindowsItem.setEnabled(hasActiveWindow);
        stackWindowsItem.setEnabled(hasActiveWindow);
        resetViewItem.setEnabled(hasActiveWindow);
        displayWindowMenuItem.setEnabled(svgWindows.size() > 0);
    }

    public static final native void log(String msg) /*-{
                                                    console.log(msg);
                                                    }-*/;

    public void inspector() {
        GWT.log("inspector()");
        if (inspectorWindow == null) {
            inspectorWindow = new InspectorWindow();
        }
        inspectorWindow.show();
        if (activeWindow != null) {
            activeWindow.updateSelectionListeners();
        }
    }

    public CssContextModel getCssContext() {
        return cssContext;
    }

    public List<MetaModel<?>> getMetaModels() {
        if (metaModels == null) {
            metaModels = new ArrayList<MetaModel<?>>();
            metaModels.add(SVGLineElementModel.getLineElementMetaModel());
            metaModels.add(SVGCircleElementModel.getCircleElementMetaModel());
            metaModels.add(SVGEllipseElementModel.getEllipseElementMetaModel());
            metaModels.add(SVGRectElementModel.getRectElementMetaModel());
            metaModels.add(SVGPolygonElementModel.getPolygonElementMetaModel());
            metaModels.add(SVGPolylineElementModel.getPolylineElementMetaModel());
            metaModels.add(SVGImageElementModel.getImageElementMetaModel());
        }
        return metaModels;
    }

    /**
     * Returns the global command factory selector
     * @return
     */
    public CommandFactorySelector getCommandFactorySelector() {
        if (commandFactorySelector == null) {
            commandFactorySelector = new CommandFactorySelector();
        }
        return commandFactorySelector;
    }

    /**
     * Returns the application event bus
     * @return the application event bus
     */
    public EventBus getEventBus() {
        if (eventBus == null) {
            eventBus = new SimpleEventBus();
        }
        return eventBus;
    }

    public CommandFactoryToolBar getCommandToolBar() {
        return commandToolBar;
    }

    public DecoratedImageCache getImageCache() {
        return imageCache;
    }

    @Override
    public void fireEvent(GwtEvent<?> event) {
        eventBus.fireEventFromSource(event, this);
    }

    @Override
    public HandlerRegistration addDeactivateWindowHandler(DeactivateWindowHandler handler) {
        return eventBus.addHandlerToSource(DeactivateWindowEvent.getType(), this, handler);
    }

    @Override
    public HandlerRegistration addActivateWindowHandler(ActivateWindowHandler handler) {
        return eventBus.addHandlerToSource(ActivateWindowEvent.getType(), this, handler);
    }

    public static SvgrealApp getApp() {
        if (instance == null) {
            instance = new SvgrealApp();
        }
        return instance;
    }
}