Java tutorial
/************************************************************************************ * 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(); } }