/** * Copyright 2004-2016 Riccardo Solmi. All rights reserved. * This file is part of the Whole Platform. * * The Whole Platform is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The Whole Platform 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the Whole Platform. If not, see <http://www.gnu.org/licenses/>. */ package org.whole.lang.e4.ui.parts; import static org.whole.lang.e4.ui.actions.IE4UIConstants.*; import java.util.ArrayList; import java.util.List; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.inject.Inject; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IWorkspace; import org.eclipse.e4.core.commands.ECommandService; import org.eclipse.e4.core.commands.EHandlerService; import org.eclipse.e4.core.contexts.ContextInjectionFactory; import org.eclipse.e4.core.contexts.IEclipseContext; import org.eclipse.e4.core.di.annotations.Optional; import org.eclipse.e4.ui.di.Focus; import org.eclipse.e4.ui.di.Persist; import org.eclipse.e4.ui.di.PersistState; import org.eclipse.e4.ui.di.UIEventTopic; import org.eclipse.e4.ui.di.UISynchronize; import org.eclipse.e4.ui.model.application.MApplication; import org.eclipse.e4.ui.model.application.ui.basic.MPart; import org.eclipse.e4.ui.model.application.ui.menu.MPopupMenu; import org.eclipse.e4.ui.model.application.ui.menu.impl.MenuFactoryImpl; import org.eclipse.e4.ui.services.EMenuService; import org.eclipse.e4.ui.services.IServiceConstants; import org.eclipse.e4.ui.workbench.modeling.EModelService; import org.eclipse.e4.ui.workbench.modeling.ESelectionService; import org.eclipse.gef.ContextMenuProvider; import org.eclipse.gef.commands.CommandStack; import org.eclipse.gef.commands.CommandStackEvent; import org.eclipse.gef.commands.CommandStackEventListener; import org.eclipse.jface.action.IContributionItem; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.FocusListener; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Composite; import org.whole.lang.bindings.IBindingManager; import org.whole.lang.codebase.IFilePersistenceProvider; import org.whole.lang.codebase.IPersistenceProvider; import org.whole.lang.commons.model.RootFragment; import org.whole.lang.e4.ui.actions.ActionRegistry; import org.whole.lang.e4.ui.actions.E4KeyHandler; import org.whole.lang.e4.ui.actions.E4NavigationKeyHandler; import org.whole.lang.e4.ui.actions.ILinkViewerListener; import org.whole.lang.e4.ui.actions.ILinkableSelectionListener; import org.whole.lang.e4.ui.actions.IE4UIConstants; import org.whole.lang.e4.ui.handler.HandlersBehavior; import org.whole.lang.e4.ui.input.ModelInput; import org.whole.lang.e4.ui.menu.JFaceMenuBuilder; import org.whole.lang.e4.ui.menu.PopupMenuProvider; import org.whole.lang.e4.ui.util.E4Utils; import org.whole.lang.model.IEntity; import org.whole.lang.ui.IUIProvider; import org.whole.lang.ui.dialogs.LazyConfirmationDialogReloader; import org.whole.lang.ui.editparts.IEntityPart; import org.whole.lang.ui.editparts.IPartFocusListener; import org.whole.lang.ui.input.IModelInput; import org.whole.lang.ui.viewers.IEntityPartViewer; /** * @author Enrico Persiani */ public abstract class AbstractE4Part { protected IEntityPartViewer viewer; protected ActionRegistry actionRegistry; protected IUIProvider<IMenuManager> contextMenuProvider; protected IResourceChangeListener resourceListener; protected ILinkableSelectionListener selectionLinkable; protected LazyConfirmationDialogReloader reloader; @Inject IEclipseContext context; @Inject ESelectionService selectionService; @Inject EHandlerService handlerService; @Inject ECommandService commandService; @Inject EMenuService menuService; @Inject EModelService modelService; @Inject MApplication application; @Inject MPart part; @Optional @Inject IModelInput modelInput; @Inject IWorkspace workspace; @PostConstruct public void createPartControl(Composite parent) { restoreState(); registerHandlers(); parent.setLayout(new FillLayout()); viewer = createEntityViewer(parent); viewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { updateSelection(E4Utils.createSelectionBindings(event, context)); } }); viewer.getControl().addFocusListener(new FocusListener() { @Override public void focusLost(FocusEvent event) { } @Override @SuppressWarnings("unchecked") public void focusGained(FocusEvent event) { context.set(IEntityPartViewer.class, viewer); context.set(ActionRegistry.class, actionRegistry); updateSelection(E4Utils.createSelectionBindings(viewer.getSelectedEditParts(), viewer, context)); } }); viewer.addPartFocusListener(new IPartFocusListener() { @SuppressWarnings("unchecked") public void focusChanged(IEntityPart oldPart, IEntityPart newPart) { updateSelection(E4Utils.createSelectionBindings(viewer.getSelectedEditParts(), viewer, context)); } }); E4KeyHandler keyHandler = new E4KeyHandler(context); keyHandler.setParent(new E4NavigationKeyHandler(context)); context.set(IEntityPartViewer.class, viewer); viewer.setKeyHandler(keyHandler); viewer.setContents(modelInput, createDefaultContents()); actionRegistry = ContextInjectionFactory.make(ActionRegistry.class, context); actionRegistry.registerKeyActions(viewer.getKeyHandler()); context.set(ActionRegistry.class, actionRegistry); configureContextMenu(); if (!E4Utils.isLegacyApplication()) viewer.getCommandStack().addCommandStackEventListener(new CommandStackEventListener() { @Override public void stackChanged(CommandStackEvent event) { if ((event.getDetail() & CommandStack.POST_MASK) != 0) part.setDirty(viewer.isDirty()); } }); reloader = new LazyConfirmationDialogReloader(viewer.getControl(), new Runnable() { @Override public void run() { viewer.setContents(modelInput, null); } }); } protected void registerHandlers() { if (E4Utils.isLegacyApplication()) HandlersBehavior.registerHandlers(handlerService); } protected abstract IEntityPartViewer createEntityViewer(Composite parent); protected void updateSelection(IBindingManager bm) { if (modelInput != null) E4Utils.defineResourceBindings(bm, modelInput); selectionService.setSelection(bm); //FIXME workaround selectionService.setSelection(bm); doesn't update the ACTIVE_SELECTION in the active context context.set(IServiceConstants.ACTIVE_SELECTION, bm); } protected void configureContextMenu() { JFaceMenuBuilder uiBuilder = ContextInjectionFactory.make(JFaceMenuBuilder.class, context); contextMenuProvider = new PopupMenuProvider<IContributionItem, IMenuManager>(uiBuilder); viewer.setContextMenu(new ContextMenuProvider(viewer) { @Override public void buildContextMenu(IMenuManager menuManager) { try { if (!getViewer().getEditDomain().isDisabled()) contextMenuProvider.populate(menuManager); } catch (Exception e) { getMenu().setVisible(false); } } }); } @Optional @Inject public void setModelInput(final IModelInput modelInput, IWorkspace workspace) { clearListeners(); if (modelInput != null) workspace.addResourceChangeListener(resourceListener = new ResourceChangeListener(modelInput)); } @PreDestroy public void clearListeners() { if (resourceListener != null && this.workspace != null) this.workspace.removeResourceChangeListener(resourceListener); if (selectionLinkable != null) selectionService.removeSelectionListener(selectionLinkable); } protected IEntity createDefaultContents() { return E4Utils.createEmptyStatusContents(); } @PersistState public void saveState() { part.getMenus().clear(); if (modelInput != null) { part.getPersistedState().put("basePersistenceKitId", modelInput.getBasePersistenceKit().getId()); part.getPersistedState().put("overridePersistenceKitId", modelInput.getOverridePersistenceKitId()); part.getPersistedState().put("filePath", modelInput.getLocation()); } } protected void restoreState() { if (part.getPersistedState().containsKey("basePersistenceKitId")) { String basePersistenceKitId = part.getPersistedState().get("basePersistenceKitId"); IModelInput modelInput = new ModelInput(context, part.getPersistedState().get("filePath"), basePersistenceKitId); modelInput.setOverridePersistenceKitId(part.getPersistedState().get("overridePersistenceKitId")); updateModelInput(modelInput); } } protected void updateModelInput(IModelInput modelInput) { context.set(IModelInput.class, this.modelInput = modelInput); } @Persist public void save() { if (modelInput != null) { workspace.removeResourceChangeListener(resourceListener); IPersistenceProvider pp = modelInput.getPersistenceProvider(); try { RootFragment rootFragment = (RootFragment) viewer.getContents().getModel(); modelInput.getPersistenceKit().writeModel(rootFragment.wGetRoot(), pp); viewer.getCommandStack().markSaveLocation(); part.setDirty(false); } catch (Exception e) { } finally { workspace.addResourceChangeListener(resourceListener); } } } @SuppressWarnings("unchecked") @Focus public void setFocus() { viewer.getControl().setFocus(); updateSelection(E4Utils.createSelectionBindings(viewer.getSelectedEditParts(), viewer, context)); } @Inject @Optional protected void refreshViewer(@UIEventTopic(IE4UIConstants.TOPIC_REFRESH_VIEWER) IEntity source) { if (source == null) getViewer().refreshNotation(); else if (source.wGetModel() == getViewer().getEntityContents().wGetModel()) getViewer().refreshNotation(source); } @Inject @Optional protected void rebuildViewer(@UIEventTopic(IE4UIConstants.TOPIC_REBUILD_VIEWER) IEntity source) { if (source == null) getViewer().rebuildNotation(); else if (source.wGetModel() == getViewer().getEntityContents().wGetModel()) getViewer().rebuildNotation(source); } @Inject @Optional protected void rebuildViewerConditional(@UIEventTopic(IE4UIConstants.TOPIC_REBUILD_VIEWER_CONDITIONAL) String resourceUri) { if (getViewer().getReferencedResources().contains(resourceUri)) getViewer().rebuildNotation(); } @Inject @Optional protected void syncOutlineSelection(@UIEventTopic(IE4UIConstants.TOPIC_SYNC_OUTLINE_SELECTION) IEntity selectedEntities) { if (selectedEntities.wSize()>0 && getViewer().getEditPartRegistry().containsKey(selectedEntities.wGet(0))) { List<IEntity> selection = new ArrayList<>(); for (int i=0, size=selectedEntities.wSize(); i<size; i++) selection.add(selectedEntities.wGet(i)); viewer.selectAndReveal(selection); } } public IEntityPartViewer getViewer() { return viewer; } protected MPopupMenu createContextMenu() { MPopupMenu contextMenu = MenuFactoryImpl.eINSTANCE.createPopupMenu(); contextMenu.setElementId(CONTEXT_MENU_ID); return contextMenu; } public void addLinkViewerListener(ILinkViewerListener listener) { if (this.selectionLinkable != null) selectionLinkable.addLinkViewerListener(listener); } public void removeLinkViewerListener(ILinkViewerListener listener) { if (this.selectionLinkable != null) selectionLinkable.removeLinkViewerListener(listener); } public void setSelectionLinkable(ILinkableSelectionListener selectionLinkable) { if (this.selectionLinkable != null) selectionService.removeSelectionListener(this.selectionLinkable); this.selectionLinkable = selectionLinkable; if (selectionLinkable != null) selectionService.addSelectionListener(selectionLinkable); } public ILinkableSelectionListener getSelectionLinkable() { return selectionLinkable; } protected void reloadContents() { context.get(UISynchronize.class).asyncExec(new Runnable() { @Override public void run() { if (viewer.isDirty() && modelInput.getPersistenceProvider() instanceof IFilePersistenceProvider) { reloader.schedule(((IFilePersistenceProvider) modelInput.getPersistenceProvider()).getStore()); } else viewer.setContents(modelInput, null); } }); } public class ResourceChangeListener implements IResourceChangeListener { private final IModelInput modelInput; public ResourceChangeListener(IModelInput modelInput) { this.modelInput = modelInput; } public void resourceChanged(IResourceChangeEvent event) { if (modelInput.getPersistenceProvider() instanceof IFilePersistenceProvider && event.getType() == IResourceChangeEvent.POST_CHANGE) { IFile file = ((IFilePersistenceProvider) modelInput.getPersistenceProvider()).getStore(); IResourceDelta member = event.getDelta().findMember(file.getFullPath()); if (member == null) return; if (member.getKind() == IResourceDelta.REMOVED && (member.getFlags() & IResourceDelta.MOVED_TO) != 0) { IFile destination = file.getWorkspace().getRoot().getFile(member.getMovedToPath()); IFilePersistenceProvider pp = new IFilePersistenceProvider(destination); ModelInput newModelInput = new ModelInput(context, pp, modelInput.getBasePersistenceKit().getId()); newModelInput.setOverridePersistenceKitId(modelInput.getOverridePersistenceKitId()); updateModelInput(newModelInput); reloadContents(); } else if (member.getKind() == IResourceDelta.CHANGED && (member.getFlags() & IResourceDelta.CONTENT) != 0) { reloadContents(); } } } } }