org.dyno.visual.swing.editors.VisualSwingEditor.java Source code

Java tutorial

Introduction

Here is the source code for org.dyno.visual.swing.editors.VisualSwingEditor.java

Source

/************************************************************************************
 * Copyright (c) 2008 William Chen.                                                 *
 *                                                                                  *
 * All rights reserved. This program and the accompanying materials are made        *
 * available under the terms of the Eclipse Public License v1.0 which accompanies   *
 * this distribution, and is available at http://www.eclipse.org/legal/epl-v10.html *
 *                                                                                  *
 * Use is subject to the terms of Eclipse Public License v1.0.                      *
 *                                                                                  *
 * Contributors:                                                                    * 
 *     William Chen - initial API and implementation.                               *
 ************************************************************************************/

package org.dyno.visual.swing.editors;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;

import org.dyno.visual.swing.VisualSwingPlugin;
import org.dyno.visual.swing.base.EditorAction;
import org.dyno.visual.swing.base.ExtensionRegistry;
import org.dyno.visual.swing.base.ISyncUITask;
import org.dyno.visual.swing.base.JavaUtil;
import org.dyno.visual.swing.designer.VisualDesigner;
import org.dyno.visual.swing.plugin.spi.ILookAndFeelAdapter;
import org.dyno.visual.swing.plugin.spi.ISourceParser;
import org.dyno.visual.swing.plugin.spi.ParserFactory;
import org.dyno.visual.swing.plugin.spi.WidgetAdapter;
import org.eclipse.albireo.core.AwtEnvironment;
import org.eclipse.albireo.core.SwingControl;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartConstants;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
import org.eclipse.ui.views.contentoutline.ContentOutline;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import org.eclipse.ui.views.properties.IPropertySheetPage;
import org.eclipse.ui.views.properties.PropertySheetPage;

/**
 * 
 * VisualSwingEditor
 * 
 * @version 1.0.0, 2008-7-3
 * @author William Chen
 */
@SuppressWarnings("unchecked")
public class VisualSwingEditor extends AbstractDesignerEditor
        implements IResourceChangeListener, ISelectionProvider, IPartListener, ISelectionListener {
    private static final String EDITOR_IMAGE = "/icons/editor.png";
    private static final int DELAYED_TIME = 250;
    private List<ISelectionChangedListener> listeners;
    private SwingControl embedded;
    private VisualDesigner designer;
    private VisualSwingOutline outline;
    private HashMap<String, EditorAction> actions;
    private ArrayList<EditorAction> actionList;
    private IJavaProject hostProject;
    private boolean isGeneratingCode;
    private ISelection selection;
    private PropertySheetPage sheetPage;
    private ScrolledComposite scrollPane;
    private PalettePage palettePage;
    private boolean isParsing;
    private boolean isCreatingDesignerUI;
    private String lnfClassname;

    public VisualSwingEditor() {
        super();
        actions = new HashMap<String, EditorAction>();
        actionList = new ArrayList<EditorAction>();
        listeners = new ArrayList<ISelectionChangedListener>();
    }

    public IJavaProject getHostProject() {
        return hostProject;
    }

    public void init(IEditorSite site, IEditorInput input) throws PartInitException {
        super.init(site, input);
        site.getWorkbenchWindow().getPartService().addPartListener(this);
        site.setSelectionProvider(this);
        site.getWorkbenchWindow().getSelectionService().addSelectionListener(this);
    }

    public Object getAdapter(Class adapter) {
        if (adapter == IContentOutlinePage.class) {
            if (outline == null && designer != null) {
                outline = new VisualSwingOutline(designer);
            }
            return outline;
        } else if (adapter == IPropertySheetPage.class) {
            if (sheetPage == null) {
                sheetPage = new WidgetPropertyPage();
                sheetPage.setPropertySourceProvider(new WidgetAdapterContentProvider());
            }
            return sheetPage;
        } else if (adapter == IPalettePage.class) {
            if (palettePage == null) {
                if (designer != null)
                    palettePage = new PalettePage(designer);
                else
                    palettePage = new PalettePage(this);
            }
            return palettePage;
        } else
            return super.getAdapter(adapter);
    }

    public boolean isDirty() {
        if (super.isDirty())
            return true;
        return !isGeneratingCode && designer != null && (designer.isLnfChanged() || designer.isDirty());
    }

    public boolean isSaveAsAllowed() {
        return super.isSaveAsAllowed();
    }

    public boolean isSaveOnCloseNeeded() {
        return super.isSaveOnCloseNeeded();
    }

    public ArrayList<EditorAction> getActions() {
        return actionList;
    }

    public VisualDesigner getDesigner() {
        return designer;
    }

    public void refreshActionState() {
        if (designer != null) {
            for (EditorAction action : actions.values())
                action.updateState();
        }
    }

    public void dispose() {
        super.dispose();
        closeRelatedView();
        ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
    }

    public void addAction(EditorAction action) {
        if (action == null) {
            actionList.add(null);
        } else if (actions.get(action.getId()) == null) {
            actions.put(action.getId(), action);
            actionList.add(action);
        }
    }

    public IAction getEditorAction(String id) {
        return actions.get(id);
    }

    public void setFocus() {
        if (embedded != null) {
            embedded.setFocus();
        }
        VisualSwingPlugin.setCurrentEditor(this);
    }

    public void changeToLnf(String className) {
        LookAndFeel lnf = UIManager.getLookAndFeel();
        if (lnf == null) {
            if (className != null) {
                setLnfClassname(className);
            }
        } else {
            String lnfName = lnf.getClass().getName();
            if (className == null) {
                className = UIManager.getCrossPlatformLookAndFeelClassName();
                if (!lnfName.equals(className))
                    setLnfClassname(className);
            } else if (!lnfName.equals(className)) {
                setLnfClassname(className);
            }
        }
    }

    public void createPartControl(Composite parent) {
        if (!isSwingComponent()) {
            switchToJavaEditor();
        } else {
            parent.setLayout(new FillLayout());
            Splitter splitter = new Splitter(parent, SWT.VERTICAL);
            Composite design = new Composite(splitter, SWT.NONE);
            splitter.setFirstControl(design);
            design.setLayout(new FillLayout());
            scrollPane = new ScrolledComposite(design, SWT.H_SCROLL | SWT.V_SCROLL);
            scrollPane.setExpandHorizontal(true);
            scrollPane.setExpandVertical(true);
            Composite designerContainer = new Composite(scrollPane, SWT.NONE);
            scrollPane.setContent(designerContainer);
            designerContainer.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_WHITE));
            designerContainer.setLayout(new FillLayout());
            embedded = new EmbeddedVisualDesigner(designerContainer);
            Composite comps = new Composite(splitter, SWT.NONE);
            splitter.setSecondControl(comps);

            comps.setLayout(new FillLayout());
            super.createPartControl(comps);
            getSite().setSelectionProvider(this);
            setTitleImage(VisualSwingPlugin.getSharedImage(EDITOR_IMAGE));
        }
    }

    @Override
    protected void createActions() {
        super.createActions();
        replaceAction(ITextEditorActionConstants.CUT);
        replaceAction(ITextEditorActionConstants.COPY);
        replaceAction(ITextEditorActionConstants.PASTE);
        replaceAction(ITextEditorActionConstants.SELECT_ALL);
        replaceAction(ITextEditorActionConstants.DELETE);
    }

    private void replaceAction(String id) {
        IAction action = getAction(id);
        setAction(id, null);
        setAction(id, new DelegateAction(this, action));
    }

    private void onEditorOpen() {
        ResourcesPlugin.getWorkspace().addResourceChangeListener(this,
                IResourceChangeEvent.POST_CHANGE | IResourceChangeEvent.POST_BUILD | IResourceChangeEvent.PRE_CLOSE
                        | IResourceChangeEvent.PRE_DELETE);
        invokeLater(new Runnable() {

            public void run() {
                validateContent();
            }
        });
        asyncRunnable(new Runnable() {

            public void run() {
                openRelatedView();
            }
        });
    }

    class CreateDesignerUIJob extends Job {
        private boolean isTimerAction;

        public CreateDesignerUIJob(boolean isTimer) {
            super(Messages.VisualSwingEditor_Designer_Creation_Job);
            setPriority(INTERACTIVE);
            this.isTimerAction = isTimer;
        }

        protected IStatus run(IProgressMonitor monitor) {
            monitor.setTaskName(Messages.VisualSwingEditor_Generating_Designer);
            try {
                createDesignerUI(monitor);
            } catch (Exception e) {
                designer.initRootWidget(null);
                designer.setError(e);
            }
            if (isTimerAction)
                validateContent();
            else
                onEditorOpen();
            isCreatingDesignerUI = false;
            designer.unlock();
            monitor.done();
            return Status.OK_STATUS;
        }
    }

    private void createDesignerUI(IProgressMonitor monitor) throws Exception {
        final IFileEditorInput file = (IFileEditorInput) getEditorInput();
        asyncRunnable(new Runnable() {

            public void run() {
                setPartName(file.getName());
                setTitleToolTip(file.getToolTipText());
            }
        });
        ParserFactory factory = ParserFactory.getDefaultParserFactory();
        if (factory == null)
            throw new Exception("No parser factory available!");
        ICompilationUnit unit = JavaCore.createCompilationUnitFrom(file.getFile());
        hostProject = unit.getJavaProject();
        ISourceParser sourceParser = factory.newParser();
        isParsing = true;
        VisualSwingPlugin.setCurrentEditor(this);
        this.designer.setCompilationUnit(unit);
        try {
            WidgetAdapter adapter = sourceParser.parse(unit, monitor);
            if (adapter != null) {
                designer.initRootWidget(adapter);
                setUpLookAndFeel(adapter.getWidget().getClass());
                designer.initNamespaceWithUnit(unit);
                refreshTree();
            } else {
                throw new Exception("This class is not a valid swing class!");
            }
        } catch (Exception e) {
            throw e;
        } finally {
            isParsing = false;
        }
    }

    private void setUpLookAndFeel(Class rootClass) {
        try {
            Field field = rootClass.getDeclaredField("PREFERRED_LOOK_AND_FEEL");
            if (field.getType() == String.class) {
                field.setAccessible(true);
                String lnf = (String) field.get(null);
                String className = UIManager.getCrossPlatformLookAndFeelClassName();
                if (lnf == null) {
                    lnf = className;
                }
                setLnfClassname(lnf);
            }
        } catch (NoSuchFieldException nsfe) {
            String className = UIManager.getCrossPlatformLookAndFeelClassName();
            setLnfClassname(className);
        } catch (Exception e) {
            VisualSwingPlugin.getLogger().error(e);
            String className = UIManager.getCrossPlatformLookAndFeelClassName();
            setLnfClassname(className);
        }
    }

    private void refreshTree() {
        asyncRunnable(new Runnable() {

            public void run() {
                if (outline != null)
                    outline.refreshTree();
            }
        });
    }

    private void safeSave(final IProgressMonitor monitor) {
        getDisplay().syncExec(new Runnable() {

            public void run() {
                doSave(monitor);
            }
        });
    }

    public void saveWithProgress() {
        try {
            IRunnableWithProgress runnable = new IRunnableWithProgress() {
                public void run(IProgressMonitor monitor) {
                    safeSave(monitor);
                }
            };
            ProgressMonitorDialog dialog = new ProgressMonitorDialog(getShell());
            dialog.run(true, true, runnable);
        } catch (Exception e) {
            VisualSwingPlugin.getLogger().error(e);
            return;
        }
    }

    public void doSave(IProgressMonitor monitor) {
        if (super.isDirty()) {
            super.doSave(monitor);
        } else {
            isGeneratingCode = true;
            try {
                IFileEditorInput file = (IFileEditorInput) getEditorInput();
                setPartName(file.getName());
                setTitleToolTip(file.getToolTipText());
                ParserFactory factory = ParserFactory.getDefaultParserFactory();
                if (factory != null) {
                    ISourceParser sourceParser = factory.newParser();
                    Component root = designer.getRoot();
                    if (root != null) {
                        WidgetAdapter rootAdapter = WidgetAdapter.getWidgetAdapter(root);
                        JavaUtil.hideMenu();
                        String lnfCN = getLnfClassname();
                        rootAdapter.setPreferredLookAndFeel(lnfCN); //$NON-NLS-1$
                        ICompilationUnit unit = sourceParser.generate(rootAdapter, monitor);
                        rootAdapter.setPreferredLookAndFeel(null); //$NON-NLS-1$
                        if (unit != null) {
                            designer.initNamespaceWithUnit(unit);
                            designer.setLnfChanged(false);
                        }
                        designer.clearDirty();
                        fireDirty();
                    }
                }
            } catch (Exception e) {
                VisualSwingPlugin.getLogger().error(e);
            }
            isGeneratingCode = false;
        }
    }

    class EmbeddedVisualDesigner extends SwingControl {
        public EmbeddedVisualDesigner(Composite parent) {
            super(parent, SWT.NONE);
        }

        protected JComponent createSwingComponent() {
            designer = new VisualDesigner(VisualSwingEditor.this, this);
            JPanel backgroundPanel = new JPanel();
            backgroundPanel.setOpaque(true);
            backgroundPanel.setLayout(new BorderLayout());
            backgroundPanel.add(designer, BorderLayout.CENTER);
            backgroundPanel.setBackground(java.awt.Color.white);
            refreshActionState();
            if (!isCreatingDesignerUI) {
                isCreatingDesignerUI = true;
                new CreateDesignerUIJob(false).schedule();
            }
            return backgroundPanel;
        }

        public Composite getLayoutAncestor() {
            return getParent();
        }

    }

    public void setLnfClassname(String lnfClassname) {
        ILookAndFeelAdapter adapter = ExtensionRegistry.getLnfAdapter(lnfClassname);
        if (adapter != null) {
            try {
                LookAndFeel oldlnf = UIManager.getLookAndFeel();
                LookAndFeel newlnf = adapter.getLookAndFeelInstance();
                if (!isLnfEqual(oldlnf, newlnf)) {
                    AwtEnvironment.runWithLnf(newlnf, new ISyncUITask() {

                        public Object doTask() throws Throwable {
                            if (designer != null) {
                                Window window = SwingUtilities.getWindowAncestor(designer);
                                if (window != null)
                                    SwingUtilities.updateComponentTreeUI(window);
                            }
                            return null;
                        }
                    });
                }
            } catch (Throwable e) {
                VisualSwingPlugin.getLogger().error(e);
            }
        }
        this.lnfClassname = lnfClassname;
    }

    private boolean isLnfEqual(LookAndFeel lnf1, LookAndFeel lnf2) {
        if (lnf1 == null) {
            if (lnf2 == null) {
                return true;
            } else {
                return false;
            }
        } else {
            if (lnf2 == null) {
                return false;
            } else {
                String lnfId1 = lnf1.getID();
                String lnfId2 = lnf2.getID();
                if (!lnfId1.equals(lnfId2)) {
                    return false;
                } else {
                    Class clazz1 = lnf1.getClass();
                    Class clazz2 = lnf2.getClass();
                    return clazz1.equals(clazz2);
                }
            }
        }
    }

    public String getLnfClassname() {
        return lnfClassname;
    }

    public void resourceChanged(IResourceChangeEvent event) {
        switch (event.getType()) {
        case IResourceChangeEvent.POST_BUILD:
        case IResourceChangeEvent.POST_CHANGE:
            if (!isGeneratingCode) {
                IResourceDelta delta = event.getDelta();
                checkChange(delta);
            }
            break;
        case IResourceChangeEvent.PRE_CLOSE:
        case IResourceChangeEvent.PRE_DELETE:
            if (!isGeneratingCode) {
                IResource resource = event.getResource();
                if (hostProject.getProject() == resource) {
                    closeRelatedView();
                    closeMe();
                }
            }
            break;
        }
    }

    private void checkChange(IResourceDelta delta) {
        switch (delta.getKind()) {
        case IResourceDelta.REMOVED:
            IPath deltaPath = delta.getFullPath();
            IPath path = ((IFileEditorInput) getEditorInput()).getFile().getFullPath();
            if (deltaPath.equals(path)) {
                closeRelatedView();
                closeMe();
            }
            break;
        case IResourceDelta.CHANGED:
            deltaPath = delta.getFullPath();
            path = ((IFileEditorInput) getEditorInput()).getFile().getFullPath();
            if (deltaPath.equals(path)) {
                if (!isCreatingDesignerUI && !designer.isLocked()) {
                    isCreatingDesignerUI = true;
                    new CreateDesignerUIJob(true).schedule();
                }
            } else {
                IResourceDelta[] children = delta
                        .getAffectedChildren(IResourceDelta.CHANGED | IResourceDelta.REMOVED);
                if (children != null && children.length > 0) {
                    for (IResourceDelta res : children) {
                        checkChange(res);
                    }
                }
            }
        }
    }

    public void doSaveAs() {
        super.doSaveAs();
    }

    public void fireDirty() {
        asyncRunnable(new Runnable() {

            public void run() {
                firePropertyChange(IWorkbenchPartConstants.PROP_DIRTY);
            }
        });
    }

    public void addSelectionChangedListener(ISelectionChangedListener listener) {
        if (!listeners.contains(listener))
            listeners.add(listener);
    }

    public ISelection getSelection() {
        if (embedded != null && embedded.isFocusControl())
            return selection;
        else
            return super.getSelectionProvider().getSelection();
    }

    public void removeSelectionChangedListener(ISelectionChangedListener listener) {
        if (listeners.contains(listener))
            listeners.remove(listener);
    }

    private void fireSelectionChanged() {
        SelectionChangedEvent evt = new SelectionChangedEvent(this, selection);
        for (ISelectionChangedListener listener : listeners) {
            listener.selectionChanged(evt);
        }
    }

    public void setSelection(ISelection selection) {
        if (this.selection == null) {
            if (selection != null && !selection.isEmpty()) {
                this.selection = selection;
                fireSelectionChanged();
            } else {
                if (outline != null)
                    outline.refreshTree();
            }
        } else {
            if (selection == null) {
                if (!this.selection.isEmpty()) {
                    this.selection = null;
                    fireSelectionChanged();
                } else {
                    if (outline != null)
                        outline.refreshTree();
                }
            } else {
                if (selection.isEmpty()) {
                    if (!this.selection.isEmpty()) {
                        this.selection = selection;
                        fireSelectionChanged();
                    } else {
                        fireSelectionChanged();
                    }
                } else {
                    if (this.selection.isEmpty()) {
                        this.selection = selection;
                        fireSelectionChanged();
                    } else if (!this.selection.equals(selection)) {
                        this.selection = selection;
                        fireSelectionChanged();
                    }
                }
            }
        }
        if (outline != null)
            outline.refreshTree();
    }

    public void validateContent() {
        if (designer != null && scrollPane != null) {
            final Dimension size = designer.getPreferredSize();
            asyncRunnable(new Runnable() {
                public void run() {
                    scrollPane.setMinSize(size.width, size.height);
                }
            });
            designer.refreshDesigner();
        }
    }

    public void asyncRunnable(Runnable runnable) {
        getDisplay().asyncExec(runnable);
    }

    private void invokeLater(Runnable runnable) {
        SwingUtilities.invokeLater(runnable);
    }

    public void partActivated(IWorkbenchPart part) {
        if (part == this) {
            delaySwingExec(DELAYED_TIME, new ChangeLnfAction());
        }
    }

    private class ChangeLnfAction implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            if (isParsing)
                return;
            changeToLnf(getLnfClassname());
            if (designer != null)
                designer.clearCapture();
        }
    }

    private void delaySwingExec(int millies, ActionListener action) {
        Timer timer = new Timer(millies, action);
        timer.setRepeats(false);
        timer.start();
    }

    public void partBroughtToTop(IWorkbenchPart part) {
        if (part == this) {
            delaySwingExec(DELAYED_TIME, new ChangeLnfAction());
        }
    }

    public void partClosed(IWorkbenchPart part) {
    }

    public void partDeactivated(IWorkbenchPart part) {
        if (part == this) {
            if (designer != null)
                designer.capture();
        }
    }

    public void partOpened(IWorkbenchPart part) {
        if (part == this) {
            delaySwingExec(DELAYED_TIME, new ChangeLnfAction());
        }
    }

    public void clearToolSelection() {
        palettePage.clearToolSelection();
    }

    public void selectionChanged(IWorkbenchPart part, ISelection selection) {
        if (part instanceof ContentOutline && !selection.isEmpty() && selection instanceof IStructuredSelection) {
            designer.clearSelection();
            for (Object obj : ((IStructuredSelection) selection).toList()) {
                if (obj instanceof Component) {
                    Component comp = (Component) obj;
                    WidgetAdapter adapter = WidgetAdapter.getWidgetAdapter(comp);
                    adapter.setSelected(true);
                }
            }
            designer.refreshDesigner();
        }
    }

    public boolean isFocusOnDesigner() {
        return embedded.isFocusControl();
    }
}