Java tutorial
/******************************************************************************* * Copyright (c) May 1, 2011 NetXForge. * * This program 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. * * This program 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 Lesser General Public * License along with this program. If not, see <http://www.gnu.org/licenses/> * * Contributors: Christophe Bouhier - initial API and implementation and/or * initial documentation *******************************************************************************/ package com.netxforge.screens.editing.base; import java.util.ArrayList; import java.util.Collection; import java.util.EventObject; import java.util.List; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.emf.common.command.BasicCommandStack; import org.eclipse.emf.common.command.CommandStackListener; import org.eclipse.emf.common.notify.AdapterFactory; import org.eclipse.emf.common.ui.viewer.IViewerProvider; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain; import org.eclipse.emf.edit.domain.EditingDomain; import org.eclipse.emf.edit.domain.IEditingDomainProvider; import org.eclipse.emf.edit.provider.AdapterFactoryItemDelegator; import org.eclipse.emf.edit.provider.ComposedAdapterFactory; import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory; import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory; import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider; import org.eclipse.emf.edit.ui.provider.UnwrappingSelectionProvider; import org.eclipse.emf.edit.ui.view.ExtendedPropertySheetPage; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IStatusLineManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; 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.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Menu; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IMemento; import org.eclipse.ui.IPartListener; import org.eclipse.ui.IPropertyListener; import org.eclipse.ui.ISaveablePart2; import org.eclipse.ui.IViewPart; import org.eclipse.ui.IViewReference; import org.eclipse.ui.IViewSite; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.XMLMemento; import org.eclipse.ui.part.ViewPart; import org.eclipse.ui.views.properties.IPropertySheetPage; import org.eclipse.ui.views.properties.IPropertySourceProvider; import com.google.common.collect.Lists; import com.netxforge.screens.editing.base.actions.IActionHandler; import com.netxforge.screens.editing.base.actions.handlers.ActionHandlerDescriptor; import com.netxforge.screens.editing.base.internal.BaseEditingActivator; import com.netxforge.screens.editing.base.util.MementoUtil; /** * A ViewPart which acts as an editor. * * It is also a SaveablePart2 so it will be participate in the workbench * dirty/save cycle. We are an editing domain providers. (Not shared). * * @author Christophe Bouhier */ public abstract class AbstractScreensViewPart extends ViewPart implements ISaveablePart2, IPartListener, IEditingDomainProvider, ISelectionProvider, IMenuListener, IViewerProvider, IPropertyListener, IPropertyChangeListener, IScreenProvider { /** * This keeps track of the selection of the view as a whole. */ protected ISelection viewSelection = StructuredSelection.EMPTY; /** * Client should implement to provide the IEditingService * * @return */ public abstract IEditingService getEditingService(); /** * Clients should implement to provide the IScreenFormService * * @return */ public abstract IScreenFormService getScreenService(); /** * Returns all visible IScreen from the workbench. */ public IScreen[] getScreens() { return doGetScreens(); // Delegate to static. } /** * @return */ private static IScreen[] doGetScreens() { IWorkbenchWindow[] workbenchWindows = PlatformUI.getWorkbench().getWorkbenchWindows(); final ArrayList<IScreen> screens = Lists.newArrayList(); for (IWorkbenchWindow wbw : workbenchWindows) { for (IWorkbenchPage iWorkbenchPage : wbw.getPages()) { for (IViewReference iViewReference : iWorkbenchPage.getViewReferences()) { IViewPart viewPart = iViewReference.getView(false); if (viewPart instanceof AbstractScreenSelector) { AbstractScreenSelector selector = (AbstractScreenSelector) viewPart; IScreen screen = selector.getScreen(); if (screen != null) { screens.add(screen); } } else { // it could also be the viewPart itself is an IScreen. if (viewPart instanceof IScreen) { screens.add((IScreen) viewPart); } } } } } return screens.toArray(new IScreen[screens.size()]); } public AbstractScreensViewPart() { createActions(); } /** * Supers should override. */ private void createActions() { } /** * Create contents of the view part. * * @param parent */ @Override public void createPartControl(Composite parent) { initializeToolBar(); } // Databinding API protected abstract void initBindings(); public void dispose() { this.getSite().getPage().removePartListener(this); // this.getEditingService().disposeData(); this.getEditingService().deactivate(this); super.dispose(); } /** * Initialize the toolbar. */ private void initializeToolBar() { @SuppressWarnings("unused") IToolBarManager tbm = getViewSite().getActionBars().getToolBarManager(); } @Override public void setFocus() { if (this.getScreen() != null) { this.getScreen().setScreenFocus(); } } private ActionHandlerDescriptor actionHandlerDescriptor; /* * The memento. */ protected IMemento memento; private ExtendedPropertySheetPage propertySheetPage; public IMemento getMemento() { return memento; } /** * The memento is delegated to the IScreen whenever it becomes active. */ @Override public void init(IViewSite site, IMemento memento) throws PartInitException { super.init(site, memento); if (memento == null) { XMLMemento newMemento = XMLMemento.createWriteRoot(MementoUtil.MEM_KEY_SCREEN_PART); this.memento = newMemento; } else { // We could have a IWorkbenchConstants.TAG_VIEW_STATE which is set, // but no child if the view part didn't close properly. this.memento = memento.getChild(MementoUtil.MEM_KEY_SCREEN_PART); if (this.memento == null) { XMLMemento newMemento = XMLMemento.createWriteRoot(MementoUtil.MEM_KEY_SCREEN_PART); this.memento = newMemento; } } addPropertyListener(this); BaseEditingActivator.getDefault().getPreferenceStore().addPropertyChangeListener(this); site.getPage().addPartListener(this); // Set the current editor as selection provider. site.setSelectionProvider(this); // FIXME, ACTION handlers should be installed dynamicly, // 1) Currently action handlers are static per Part // 2) Each viewer in a part gets the same menu. (Actually for each // viewer, a menu is created). // 3) The action bar is initiated statically, so changing the viewer in // an IScreen, // doesn't update the i.e. the ActionBar which holds global actions. // If we change to a text based viewer, the global actions delete, cut, // copy, paste, select-all are EMF Actions to // deal with StructuredViewer. // Add some static action handlers which are updated by the // selection // provider of the active part. which is this. We can also add // dynamic action handlers. IActionHandler[] handlers = getActionHandlers(); actionHandlerDescriptor = new ActionHandlerDescriptor(); setHandlers(handlers); // hookPageSelection(); actionHandlerDescriptor.initActions(site.getActionBars()); getEditingDomain().getCommandStack().addCommandStackListener(cmdStackListener); } private void setHandlers(IActionHandler[] handlers) { for (IActionHandler h : handlers) { actionHandlerDescriptor.addHandler(h); } } protected IActionHandler[] getActionHandlers() { IActionHandler[] handlers = new IActionHandler[] {}; // handlers[0] = new ObjectEditingActionsHandler(getEditingService()); // handlers[1] = new CreationActionsHandler(); // handlers[2] = new UIActionsHandler(); return handlers; } public ActionHandlerDescriptor getActionHandlerDescriptor() { return actionHandlerDescriptor; } // UI states are delegated to the the IScreen interface, save the result in // our memento. public void saveState(IMemento memento) { IScreenFormService screenService = getScreenService(); if (this.getScreen() != null) { if (screenService != null) { screenService.saveScreenState(this.getScreen()); } MementoUtil.rememberString(this.getMemento(), this.getScreen().getScreenName(), MementoUtil.MEM_KEY_CURRENT_SCREEN); } // Write a pre-created child memento to the ViewState memento, for // the next time we meet. if (memento.getChild(MementoUtil.MEM_KEY_SCREEN_PART) == null) { IMemento createChild = memento.createChild(MementoUtil.MEM_KEY_SCREEN_PART); createChild.putMemento(this.getMemento()); } } /** * We deal with objects in resources outside our own editing domain. */ public void doSave(IProgressMonitor monitor) { // Delegate to a pluggable service. // getEditingService().doSave(monitor); firePropertyChange(ISaveablePart2.PROP_DIRTY); } public void doSaveAs() { throw new UnsupportedOperationException("Save As, not supported"); } // @Override // public Object getAdapter(@SuppressWarnings("rawtypes") Class key) { // if (key.equals(ISaveablePart2.class)) { // return this; // } // return super.getAdapter(key); // } /** * Delegates to the editing service. */ public boolean isDirty() { // Should know about the type of screen, so read-only, is not asked for // dirtynes.. if (this.getScreen() != null && !ScreenUtil.isReadOnlyOperation(this.getScreen().getOperation())) { return getEditingService().isDirty(); } else { return false; } } public boolean isSaveAsAllowed() { return false; } public boolean isSaveOnCloseNeeded() { return true; } public int promptToSaveOnClose() { return ISaveablePart2.PROP_DIRTY; } public void propertyChanged(Object source, int propId) { this.updateActiveScreenDirtyNess(); } public void updateActiveScreenDirtyNess() { if (this.getScreen() == null || this.getScreen().getScreenForm().isDisposed()) { return; } IScreen screen = getScreen(); String newTitle = ""; if (screen.getScreenForm() == null) { return; } String currentTitle = screen.getScreenForm().getText(); if (getEditingService().isDirty()) { if (currentTitle.endsWith(" * ")) { return; } newTitle = currentTitle.concat(" * "); } else { if (currentTitle.endsWith(" * ")) { newTitle = currentTitle.substring(0, currentTitle.indexOf(" * ")); } else { newTitle = currentTitle; } } screen.getScreenForm().setText(newTitle); } /** * An event for part activity * * @author Christophe Bouhier */ protected enum PART_EVENT { ACTIVATED, TOTOP, CLOSED, OPENEND, DEACTIVATE } /** * Called with corresponding {@link #PART_EVENT} set, clients should * implement. * * @param part */ protected abstract void customPartHook(IWorkbenchPart part, PART_EVENT event); /** * Update the action handler descriptors with the active part. */ public void partActivated(IWorkbenchPart part) { // Register selection listeners. if (part == this) { // Activate our global actions. this.getActionHandlerDescriptor().setActivePart(part); } customPartHook(part, PART_EVENT.ACTIVATED); } public void partBroughtToTop(IWorkbenchPart part) { customPartHook(part, PART_EVENT.TOTOP); } public void partClosed(IWorkbenchPart part) { customPartHook(part, PART_EVENT.CLOSED); } public void partDeactivated(IWorkbenchPart part) { customPartHook(part, PART_EVENT.DEACTIVATE); } public void partOpened(IWorkbenchPart part) { customPartHook(part, PART_EVENT.OPENEND); } @Override public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) { if (adapter.equals(IPropertySheetPage.class)) { return getPropertySheetPage(new AdapterFactoryContentProvider(getAdapterFactory())); } return super.getAdapter(adapter); } // The declared EMF edit adapter factory. static ComposedAdapterFactory emfEditAdapterFactory; /** * @return */ public static AdapterFactory getAdapterFactory() { if (emfEditAdapterFactory == null) { emfEditAdapterFactory = new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE); emfEditAdapterFactory.addAdapterFactory(new ResourceItemProviderAdapterFactory()); emfEditAdapterFactory.addAdapterFactory(new ReflectiveItemProviderAdapterFactory()); } return emfEditAdapterFactory; } /** * This accesses a cached version of the property sheet. <!-- begin-user-doc * --> <!-- end-user-doc --> * * @generated */ public IPropertySheetPage getPropertySheetPage(IPropertySourceProvider propertySourceProvider) { if (propertySheetPage == null) { propertySheetPage = new ExtendedPropertySheetPage( (AdapterFactoryEditingDomain) this.getEditingDomain()) { @Override public void setSelectionToViewer(List<?> selection) { // CB, We don't have this method. // The default EMF implementation calls setSelection on the // current Viewer with an Async call. // In our case, it would be setSelectionToScreen. The // current viewer in the screen // AbstractScreensViewPart.this.setSelectionToViewer(selection); // Convert the selection, to ISelection like in // setSelectionToViewer. // AbstractScreensViewPart.this.getScreen().setSelection(selection); AbstractScreensViewPart.this.setFocus(); } @Override public void setActionBars(IActionBars actionBars) { super.setActionBars(actionBars); // CB We don't have a A contributor to share the actions // with the property sheet page. // Investigate. // getActionBarContributor().shareGlobalActions(this, // actionBars); } }; propertySheetPage.setPropertySourceProvider(propertySourceProvider); } return propertySheetPage; } // BasicCommandStack commandStack = new BasicCommandStack(); // Add a listener to set the viewer dirty state. final CommandStackListener cmdStackListener = new CommandStackListener() { public void commandStackChanged(final EventObject event) { // Note this also fires when flushing the command stack, as // this is executed async, the widget is disposed so bail if there // is no recent command if (event.getSource() instanceof BasicCommandStack) { BasicCommandStack stack = (BasicCommandStack) event.getSource(); if (stack.getMostRecentCommand() == null) { return; } } getViewSite().getShell().getDisplay().asyncExec(new Runnable() { public void run() { // We should not dirty mark, while editing // or new a single object. firePropertyChange(ISaveablePart2.PROP_DIRTY); getEditingService().setDirty(); // FIXME Some views don't have data binding, // so we need to refresh the input. // currentViewer.refresh(); if (BaseEditingActivator.DEBUG) { BaseEditingActivator.TRACE.trace(null, "Command stack changed"); // System.out // .println("Command stack, fire command stack changed, source=" // + event.getSource()); } } }); } }; // IEditingDomainProvider API. // AdapterFactoryEditingDomain domain = null; public EditingDomain getEditingDomain() { return getEditingService().getEditingDomain(); } public void publicFirePropertyChange(int propertyId) { this.firePropertyChange(propertyId); } // ISelectionProvider API. // In our case, our globalActionsHnadler actions listen to our selection // provider when // the part is is activated. // Whatever screen is active, should provide selection. protected Collection<ISelectionChangedListener> selectionChangedListeners = new ArrayList<ISelectionChangedListener>(); public void addSelectionChangedListener(ISelectionChangedListener listener) { if (!selectionChangedListeners.contains(listener)) { if (selectionChangedListeners.add(listener)) { // System.out.println("Listener: " + listener + " added to: " // + this); } } } public ISelection getSelection() { return viewSelection; } public void removeSelectionChangedListener(ISelectionChangedListener listener) { if (!selectionChangedListeners.remove(listener)) { // System.out.println("Listener: " + listener + // " not removed from: " // + this); } else { // System.out.println("Listener: " + listener + " removed from: " // + this); } } /** * Update to pass on the original {@link SelectionChangedEvent event} * * @param sce */ public void setSelection(SelectionChangedEvent sce) { this.viewSelection = sce.getSelection(); for (ISelectionChangedListener listener : selectionChangedListeners) { listener.selectionChanged(sce); } // Set the status, either selection or the main object handled by the // screen. if (!viewSelection.isEmpty()) { setStatusLineManager(viewSelection); } else { IScreen screen = this.getScreen(); setStatusLineManager(screen.getScreenObjects()); } } public void setSelection(ISelection selection) { this.viewSelection = selection; for (ISelectionChangedListener listener : selectionChangedListeners) { listener.selectionChanged(new SelectionChangedEvent(this, selection)); } // Set the status, either selection or the main object handled by the // screen. if (!selection.isEmpty()) { setStatusLineManager(selection); } else { IScreen screen = this.getScreen(); setStatusLineManager(screen.getScreenObjects()); } } protected void setStatusLineManager(Collection<? extends Object> screenObjects) { String message = ""; switch (screenObjects.size()) { case 0: { message = "No objects"; break; } case 1: { Object next = screenObjects.iterator().next(); if (next instanceof EObject) { @SuppressWarnings("unused") EObject eo = (EObject) next; // Do EObject have a unique ID? } String text = new AdapterFactoryItemDelegator(getAdapterFactory()).getText(next); message = "Screen object: " + text; // message = "Screen object: " + text + " OID:" + cdoID; break; } default: { message = "Objects: " + Integer.toString(screenObjects.size()); break; } } // System.out.println("status message: " + message); // for(StackTraceElement se : Thread.currentThread().getStackTrace()){ // System.out.println("--" + se.toString()); // } setStatusLineManager(message); } /** * <!-- begin-user-doc --> <!-- end-user-doc --> * * @generated */ private void setStatusLineManager(ISelection selection) { if (selection instanceof IStructuredSelection) { @SuppressWarnings("unchecked") List<Object> newArrayList = Lists.newArrayList(((IStructuredSelection) selection).iterator()); setStatusLineManager(newArrayList); } } protected void setStatusLineManager(String message) { IStatusLineManager statusLineManager = this.getViewSite().getActionBars().getStatusLineManager(); if (statusLineManager != null) { statusLineManager.setMessage(message); } } /** * This creates a context menu for the viewer and adds a listener as well * registering the menu for extension. <!-- begin-user-doc --> <!-- * end-user-doc --> */ protected void augmentContextMenuFor(StructuredViewer viewer) { MenuManager contextMenu = new MenuManager("#PopUp"); contextMenu.add(new Separator("additions")); contextMenu.setRemoveAllWhenShown(true); contextMenu.addMenuListener(this); Menu menu = contextMenu.createContextMenu(viewer.getControl()); viewer.getControl().setMenu(menu); getSite().registerContextMenu(contextMenu, new UnwrappingSelectionProvider(viewer)); } public void menuAboutToShow(IMenuManager manager) { // CB We might not be the active part, make sure we are activated. // this is important for show-in, which activates the show-in part. IWorkbenchPart activePart = this.getSite().getWorkbenchWindow().getActivePage().getActivePart(); if (activePart != this) { this.getSite().getWorkbenchWindow().getActivePage().activate(this); } contributeMenuAboutToShow(manager); } /** * Implementors should populate the given {@link IMenuManager } with a * context menu for an IScreen. * * @param manager */ public abstract void contributeMenuAboutToShow(IMenuManager manager); /** * This listens to which ever viewer is active. */ protected ISelectionChangedListener selectionChangedListener; /** * This keeps track of the active content viewer, which may be either one of * the viewers in the screens. * * @deprecated */ protected Viewer currentViewer; /** * Call from which ever screen with multiple viewers which can/should * provide selection. * * @param viewer */ public void setCurrentScreen(IScreen screen) { if (selectionChangedListener == null) { // Create the listener on demand. // selectionChangedListener = new ISelectionChangedListener() { // This just notifies those things that are affected by the // section. // public void selectionChanged(SelectionChangedEvent selectionChangedEvent) { setSelection(selectionChangedEvent); } }; } // Stop listening to the old one. // if (getScreen() != null) { getScreen().removeSelectionChangedListener(selectionChangedListener); } // Start listening to the new one. // if (screen != null) { screen.addSelectionChangedListener(selectionChangedListener); } // Set the editors selection based on the current screen's // selection. setSelection(screen == null ? StructuredSelection.EMPTY : screen.getSelection()); Viewer viewer = screen.getViewer(); if (viewer instanceof StructuredViewer) { // Don't do that yet, as we have no facility to learn the // existing menu items. augmentContextMenuFor((StructuredViewer) viewer); } // Install a menu on the active viewers. // for (Viewer v : screen.getViewers()) { // // Install a context menu, for all possible viewers, note // // all actions will be installed, we don't differentiate which // // actions are added to which viewer. // if (v instanceof StructuredViewer) { // // Don't do that yet, as we have no facility to learn the // // existing menu items. // augmentContextMenuFor((StructuredViewer) v); // } // } } /** * Call from which ever screen with a view which can/should provide * selection. * * @param viewer * @deprecated Use setCurrentScreen instead. */ public void setCurrentViewer(Viewer viewer) { if (currentViewer != viewer) { if (selectionChangedListener == null) { // Create the listener on demand. // selectionChangedListener = new ISelectionChangedListener() { // This just notifies those things that are affected by the // section. // public void selectionChanged(SelectionChangedEvent selectionChangedEvent) { setSelection(selectionChangedEvent.getSelection()); } }; } // Stop listening to the old one. // if (currentViewer != null) { currentViewer.removeSelectionChangedListener(selectionChangedListener); } // Start listening to the new one. // if (viewer != null) { viewer.addSelectionChangedListener(selectionChangedListener); } // Remember it. // currentViewer = viewer; // Set the editors selection based on the current viewer's // selection. // setSelection(currentViewer == null ? StructuredSelection.EMPTY : currentViewer.getSelection()); // Install a context menu. if (currentViewer instanceof StructuredViewer) { // Don't do that yet, as we have no facility to learn the // existing menu items. augmentContextMenuFor((StructuredViewer) viewer); } } } /** * Get the current viewer. * * @deprecated */ public Viewer getViewer() { return currentViewer; } /** * Clients can override to be notified of {@link PropertyChangeEvent} */ public void propertyChange(PropertyChangeEvent event) { } }