| /******************************************************************************* |
| * Copyright (c) 2010, 2018 BestSolution.at and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation |
| * Wim Jongman <wim.jongman@remainsoftware.com> - Maintenance |
| * Marco Descher <marco@descher.at> - Bug395982, 426653, 422465, 429674 |
| * Lars Vogel <Lars.Vogel@gmail.com> - Ongoing maintenance |
| * Steven Spungin <steven@spungin.tv> - Bug 396902, 431755, 431735, 424730, 424730, 391089, 437236, 437552, Ongoing |
| * Maintenance |
| * Simon Scholz <simon.scholz@vogella.com> - Bug 475365 |
| * Olivier Prouvost <olivier.prouvost@opcoach.com> - Bug 472706, 429684, 531451 |
| * Dmitry Spiridenok <d.spiridenok@gmail.com> - Bug 429684 |
| ******************************************************************************/ |
| package org.eclipse.e4.tools.emf.ui.internal.common; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| |
| import javax.annotation.PostConstruct; |
| import javax.annotation.PreDestroy; |
| import javax.inject.Inject; |
| import javax.inject.Named; |
| |
| import org.eclipse.core.databinding.ObservablesManager; |
| import org.eclipse.core.databinding.observable.list.IObservableList; |
| import org.eclipse.core.databinding.observable.list.WritableList; |
| import org.eclipse.core.databinding.observable.map.IObservableMap; |
| import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; |
| import org.eclipse.core.databinding.observable.set.WritableSet; |
| import org.eclipse.core.databinding.property.list.IListProperty; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.IExtensionPoint; |
| import org.eclipse.core.runtime.IExtensionRegistry; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.RegistryFactory; |
| import org.eclipse.core.runtime.preferences.IEclipsePreferences; |
| import org.eclipse.e4.core.contexts.ContextInjectionFactory; |
| import org.eclipse.e4.core.contexts.IEclipseContext; |
| import org.eclipse.e4.core.di.annotations.Execute; |
| import org.eclipse.e4.core.di.annotations.Optional; |
| import org.eclipse.e4.core.di.extensions.Preference; |
| import org.eclipse.e4.core.services.contributions.IContributionFactory; |
| import org.eclipse.e4.core.services.log.Logger; |
| import org.eclipse.e4.core.services.nls.Translation; |
| import org.eclipse.e4.core.services.translation.TranslationService; |
| import org.eclipse.e4.tools.emf.ui.common.AbstractElementEditorContribution; |
| import org.eclipse.e4.tools.emf.ui.common.IContributionClassCreator; |
| import org.eclipse.e4.tools.emf.ui.common.IEditorDescriptor; |
| import org.eclipse.e4.tools.emf.ui.common.IEditorFeature; |
| import org.eclipse.e4.tools.emf.ui.common.IEditorFeature.FeatureClass; |
| import org.eclipse.e4.tools.emf.ui.common.IExtensionLookup; |
| import org.eclipse.e4.tools.emf.ui.common.IModelExtractor; |
| import org.eclipse.e4.tools.emf.ui.common.IModelResource; |
| import org.eclipse.e4.tools.emf.ui.common.IScriptingSupport; |
| import org.eclipse.e4.tools.emf.ui.common.MemoryTransfer; |
| import org.eclipse.e4.tools.emf.ui.common.ModelEditorPreferences; |
| import org.eclipse.e4.tools.emf.ui.common.Plugin; |
| import org.eclipse.e4.tools.emf.ui.common.Util; |
| import org.eclipse.e4.tools.emf.ui.common.component.AbstractComponentEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.Messages; |
| import org.eclipse.e4.tools.emf.ui.internal.ResourceProvider; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.AddonsEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.ApplicationEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.AreaEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.BindingContextEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.BindingTableEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.CategoryEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.CommandEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.CommandParameterEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.CompositePartEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.CoreExpressionEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.DefaultEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.DirectMenuItemEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.DirectToolItemEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.DynamicMenuContributionEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.HandledMenuItemEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.HandledToolItemEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.HandlerEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.ImperativeExpressionEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.KeyBindingEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.MenuContributionEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.MenuEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.MenuSeparatorEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.ModelFragmentsEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.ParameterEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.PartDescriptorEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.PartEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.PartSashContainerEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.PartStackEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.PerspectiveEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.PerspectiveStackEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.PlaceholderEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.PopupMenuEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.StringModelFragment; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.ToolBarContributionEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.ToolBarEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.ToolBarSeparatorEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.ToolControlEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.TrimBarEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.TrimContributionEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.TrimmedWindowEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.WindowEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.tabs.EmfUtil; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.tabs.IGotoObject; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.tabs.ListTab; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.tabs.XmiTab; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VApplicationAddons; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VApplicationCategoriesEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VApplicationWindowEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VBindingTableEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VCommandEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VControlsEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VHandlerEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VItemParametersEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VMenuContributionsEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VModelFragmentsEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VModelImportsEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VPartDescriptor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VPartDescriptorMenuEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VPartDescriptorTrimEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VPartMenuEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VPartTrimEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VPerspectiveControlEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VPerspectiveTrimEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VPerspectiveWindowsEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VRootBindingContexts; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VSnippetsEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VToolBarContributionsEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VTrimContributionsEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VWindowControlEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VWindowSharedElementsEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VWindowTrimEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VWindowWindowsEditor; |
| import org.eclipse.e4.tools.emf.ui.internal.common.properties.ExportIdsHandler; |
| import org.eclipse.e4.tools.emf.ui.internal.common.properties.ExternalizeStringHandler; |
| import org.eclipse.e4.tools.emf.ui.internal.common.properties.ProjectOSGiTranslationProvider; |
| import org.eclipse.e4.tools.emf.ui.internal.common.xml.EMFDocumentResourceMediator; |
| import org.eclipse.e4.tools.services.IClipboardService; |
| import org.eclipse.e4.tools.services.IClipboardService.Handler; |
| import org.eclipse.e4.tools.services.IResourcePool; |
| 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.dialogs.filteredtree.FilteredTree; |
| import org.eclipse.e4.ui.dialogs.filteredtree.PatternFilter; |
| import org.eclipse.e4.ui.model.application.MApplication; |
| import org.eclipse.e4.ui.model.application.MApplicationElement; |
| import org.eclipse.e4.ui.model.application.commands.impl.CommandsPackageImpl; |
| import org.eclipse.e4.ui.model.application.impl.ApplicationElementImpl; |
| import org.eclipse.e4.ui.model.application.impl.ApplicationPackageImpl; |
| import org.eclipse.e4.ui.model.application.ui.MContext; |
| import org.eclipse.e4.ui.model.application.ui.MElementContainer; |
| import org.eclipse.e4.ui.model.application.ui.MUIElement; |
| import org.eclipse.e4.ui.model.application.ui.MUILabel; |
| import org.eclipse.e4.ui.model.application.ui.advanced.impl.AdvancedPackageImpl; |
| import org.eclipse.e4.ui.model.application.ui.basic.MPart; |
| import org.eclipse.e4.ui.model.application.ui.basic.impl.BasicPackageImpl; |
| import org.eclipse.e4.ui.model.application.ui.impl.UiPackageImpl; |
| import org.eclipse.e4.ui.model.application.ui.menu.impl.MenuPackageImpl; |
| import org.eclipse.e4.ui.model.fragment.MModelFragment; |
| import org.eclipse.e4.ui.model.fragment.MModelFragments; |
| import org.eclipse.e4.ui.model.fragment.MStringModelFragment; |
| import org.eclipse.e4.ui.model.fragment.impl.FragmentPackageImpl; |
| import org.eclipse.e4.ui.model.internal.ModelUtils; |
| import org.eclipse.e4.ui.services.IServiceConstants; |
| import org.eclipse.e4.ui.workbench.UIEvents; |
| import org.eclipse.e4.ui.workbench.UIEvents.EventTags; |
| import org.eclipse.e4.ui.workbench.modeling.ESelectionService; |
| import org.eclipse.emf.common.command.AbstractCommand; |
| import org.eclipse.emf.common.command.Command; |
| import org.eclipse.emf.common.command.CompoundCommand; |
| import org.eclipse.emf.databinding.EMFProperties; |
| import org.eclipse.emf.databinding.FeaturePath; |
| import org.eclipse.emf.databinding.IEMFProperty; |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EClassifier; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.eclipse.emf.edit.command.AddCommand; |
| import org.eclipse.emf.edit.command.CommandParameter; |
| import org.eclipse.emf.edit.command.DeleteCommand; |
| import org.eclipse.emf.edit.command.MoveCommand; |
| import org.eclipse.emf.edit.command.RemoveCommand; |
| import org.eclipse.emf.edit.domain.EditingDomain; |
| import org.eclipse.jface.action.Action; |
| import org.eclipse.jface.action.MenuManager; |
| import org.eclipse.jface.action.Separator; |
| import org.eclipse.jface.databinding.viewers.ObservableListTreeContentProvider; |
| import org.eclipse.jface.databinding.viewers.TreeStructureAdvisor; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.resource.FontDescriptor; |
| import org.eclipse.jface.resource.ImageDescriptor; |
| import org.eclipse.jface.resource.ImageRegistry; |
| import org.eclipse.jface.resource.JFaceResources; |
| import org.eclipse.jface.resource.StringConverter; |
| import org.eclipse.jface.viewers.AbstractTreeViewer; |
| import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.ITreeViewerListener; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.jface.viewers.TreeExpansionEvent; |
| import org.eclipse.jface.viewers.TreeViewer; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.jface.viewers.ViewerDropAdapter; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.CTabFolder; |
| import org.eclipse.swt.custom.CTabItem; |
| import org.eclipse.swt.custom.SashForm; |
| import org.eclipse.swt.custom.StackLayout; |
| import org.eclipse.swt.dnd.Clipboard; |
| import org.eclipse.swt.dnd.DND; |
| import org.eclipse.swt.dnd.DragSourceAdapter; |
| import org.eclipse.swt.dnd.DragSourceEvent; |
| import org.eclipse.swt.dnd.Transfer; |
| import org.eclipse.swt.dnd.TransferData; |
| import org.eclipse.swt.events.KeyAdapter; |
| import org.eclipse.swt.events.KeyEvent; |
| import org.eclipse.swt.events.KeyListener; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.events.TreeAdapter; |
| import org.eclipse.swt.events.TreeEvent; |
| import org.eclipse.swt.graphics.RGB; |
| import org.eclipse.swt.layout.FillLayout; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Listener; |
| import org.eclipse.swt.widgets.TreeItem; |
| import org.eclipse.ui.forms.widgets.FormToolkit; |
| import org.eclipse.ui.forms.widgets.Section; |
| |
| public class ModelEditor implements IGotoObject { |
| private static final String ORG_ECLIPSE_E4_TOOLS_MODELEDITOR_FILTEREDTREE_ENABLED_XMITAB_DISABLED = "org.eclipse.e4.tools.modeleditor.filteredtree.enabled.xmitab.disabled";//$NON-NLS-1$ |
| |
| public static final String CSS_CLASS_KEY = "org.eclipse.e4.ui.css.CssClassName"; //$NON-NLS-1$ |
| |
| public static final String VIRTUAL_PART_MENU = "org.eclipse.e4.tools.emf.ui.VIRTUAL_PART_MENU"; //$NON-NLS-1$ |
| public static final String VIRTUAL_HANDLER = "org.eclipse.e4.tools.emf.ui.VIRTUAL_HANDLER"; //$NON-NLS-1$ |
| public static final String VIRTUAL_CONTROLS = "org.eclipse.e4.tools.emf.ui.VIRTUAL_CONTROLS"; //$NON-NLS-1$ |
| public static final String VIRTUAL_BINDING_TABLE = "org.eclipse.e4.tools.emf.ui.VIRTUAL_BINDING_TABLE"; //$NON-NLS-1$ |
| public static final String VIRTUAL_COMMAND = "org.eclipse.e4.tools.emf.ui.VIRTUAL_COMMAND"; //$NON-NLS-1$ |
| public static final String VIRTUAL_APPLICATION_WINDOWS = "org.eclipse.e4.tools.emf.ui.VIRTUAL_APPLICATION_WINDOWS"; //$NON-NLS-1$ |
| public static final String VIRTUAL_PERSPECTIVE_WINDOWS = "org.eclipse.e4.tools.emf.ui.VIRTUAL_PERSPECTIVE_WINDOWS"; //$NON-NLS-1$ |
| public static final String VIRTUAL_WINDOW_WINDOWS = "org.eclipse.e4.tools.emf.ui.VIRTUAL_WINDOW_WINDOWS"; //$NON-NLS-1$ |
| public static final String VIRTUAL_WINDOW_CONTROLS = "org.eclipse.e4.tools.emf.ui.VIRTUAL_WINDOW_CONTROLS"; //$NON-NLS-1$ |
| public static final String VIRTUAL_PART_TRIMS = "org.eclipse.e4.tools.emf.ui.VIRTUAL_PART_TRIMS"; //$NON-NLS-1$ |
| public static final String VIRTUAL_PART_DESCRIPTORS = "org.eclipse.e4.tools.emf.ui.VIRTUAL_PART_DESCRIPTORS"; //$NON-NLS-1$ |
| public static final String VIRTUAL_PARTDESCRIPTOR_MENU = "org.eclipse.e4.tools.emf.ui.VIRTUAL_PARTDESCRIPTOR_MENU"; //$NON-NLS-1$ |
| public static final String VIRTUAL_PARTDESCRIPTOR_TRIMS = "org.eclipse.e4.tools.emf.ui.VIRTUAL_PART_DESCRIPTOR_TRIMS"; //$NON-NLS-1$ |
| public static final String VIRTUAL_TRIMMED_WINDOW_TRIMS = "org.eclipse.e4.tools.emf.ui.VIRTUAL_TRIMMED_WINDOW_TRIMS"; //$NON-NLS-1$ |
| public static final String VIRTUAL_ADDONS = "org.eclipse.e4.tools.emf.ui.VIRTUAL_ADDONS"; //$NON-NLS-1$ |
| public static final String VIRTUAL_MENU_CONTRIBUTIONS = "org.eclipse.e4.tools.emf.ui.VIRTUAL_MENU_CONTRIBUTIONS"; //$NON-NLS-1$ |
| public static final String VIRTUAL_TOOLBAR_CONTRIBUTIONS = "org.eclipse.e4.tools.emf.ui.VIRTUAL_TOOLBAR_CONTRIBUTIONS"; //$NON-NLS-1$ |
| public static final String VIRTUAL_TRIM_CONTRIBUTIONS = "org.eclipse.e4.tools.emf.ui.VIRTUAL_TRIM_CONTRIBUTIONS"; //$NON-NLS-1$ |
| public static final String VIRTUAL_WINDOW_SHARED_ELEMENTS = "org.eclipse.e4.tools.emf.ui.VIRTUAL_WINDOW_SHARED_ELEMENTS"; //$NON-NLS-1$ |
| public static final String VIRTUAL_WINDOW_SNIPPETS = "org.eclipse.e4.tools.emf.ui.VIRTUAL_WINDOW_SNIPPETS"; //$NON-NLS-1$ |
| |
| public static final String VIRTUAL_MODEL_FRAGEMENTS = "org.eclipse.e4.tools.emf.ui.VIRTUAL_MODEL_FRAGEMENTS"; //$NON-NLS-1$ |
| public static final String VIRTUAL_MODEL_IMPORTS = "org.eclipse.e4.tools.emf.ui.VIRTUAL_MODEL_IMPORTS"; //$NON-NLS-1$ |
| public static final String VIRTUAL_CATEGORIES = "org.eclipse.e4.tools.emf.ui.VIRTUAL_CATEGORIES"; //$NON-NLS-1$ |
| public static final String VIRTUAL_PARAMETERS = "org.eclipse.e4.tools.emf.ui.VIRTUAL_PARAMETERS"; //$NON-NLS-1$ |
| public static final String VIRTUAL_MENUELEMENTS = "org.eclipse.e4.tools.emf.ui.VIRTUAL_MENUELEMENTS"; //$NON-NLS-1$ |
| public static final String VIRTUAL_ROOT_CONTEXTS = "org.eclipse.e4.tools.emf.ui.VIRTUAL_ROOT_CONTEXTS"; //$NON-NLS-1$ |
| public static final String VIRTUAL_PERSPECTIVE_CONTROLS = "org.eclipse.e4.tools.emf.ui.VIRTUAL_PERSPECTIVE_CONTROLS"; //$NON-NLS-1$ |
| public static final String VIRTUAL_PERSPECTIVE_TRIMS = "org.eclipse.e4.tools.emf.ui.VIRTUAL_PERSPECTIVE_TRIMS"; //$NON-NLS-1$ |
| public static final String VIRTUAL_SNIPPETS = "org.eclipse.e4.tools.emf.ui.VIRTUAL_SNIPPETS"; //$NON-NLS-1$ |
| |
| public static final int TAB_FORM = 0; |
| public static final int TAB_XMI = 1; |
| public static final int TAB_LIST = 2; |
| |
| |
| |
| /** |
| * A map with key = eClass name or virtual key, value is an |
| * AbstractComponentEditor instance This map is filled on the fly when getting |
| * editors |
| */ |
| private final Map<String, AbstractComponentEditor<?>> editors = new HashMap<>(); |
| |
| /** |
| * A map with key = eClass name or virtual key, value is a class of |
| * AbstractComponentEditor. This map is filled on init by registerEditor |
| */ |
| private final Map<String, Class<? extends AbstractComponentEditor<?>>> editorsClasses = new HashMap<>(); |
| |
| private final Map<Class<?>, List<AbstractElementEditorContribution>> tabContributions = new HashMap<>(); |
| private final List<FeaturePath> labelFeaturePaths = new ArrayList<>(); |
| private final List<IEditorFeature> editorFeatures = new ArrayList<>(); |
| private final List<IContributionClassCreator> contributionCreator = new ArrayList<>(); |
| |
| private TreeViewer viewer; |
| private final IModelResource modelProvider; |
| private final IProject project; |
| |
| /** An imageRegistry for dynamic component images (see bug #403583) */ |
| private final ImageRegistry componentImages = new ImageRegistry(); |
| |
| @Inject |
| ESelectionService selectionService; |
| |
| @Inject |
| MApplication app; |
| |
| private final IEclipseContext context; |
| private boolean fragment; |
| private Handler clipboardHandler; |
| |
| @Inject |
| @Optional |
| private IClipboardService clipboardService; |
| |
| @Inject |
| @Preference(nodePath = Plugin.ID, value = ModelEditorPreferences.AUTO_CREATE_ELEMENT_ID) |
| private boolean autoCreateElementId; |
| |
| @Inject |
| @Preference(nodePath = Plugin.ID, value = ModelEditorPreferences.SHOW_XMI_ID) |
| private boolean showXMIId; |
| |
| @Inject |
| @Preference(nodePath = Plugin.ID) |
| IEclipsePreferences preferences; |
| |
| @Inject |
| @Optional |
| private IExtensionLookup extensionLookup; |
| |
| @Inject |
| @Translation |
| private Messages messages; |
| |
| @Inject |
| @Optional |
| private IModelExtractor modelExtractor; |
| |
| @Inject |
| @Optional |
| MPart currentPart; |
| |
| @Inject |
| private Logger logger; |
| |
| private final ObservablesManager obsManager; |
| |
| private final IResourcePool resourcePool; |
| |
| private EMFDocumentResourceMediator emfDocumentProvider; |
| |
| private AbstractComponentEditor<?> currentEditor; |
| |
| private Listener keyListener; |
| |
| private CTabFolder editorTabFolder; |
| |
| private boolean mod1Down = false; |
| |
| private boolean saving; |
| |
| private ListTab listTab; |
| |
| private CTabItem tabItemXmi; |
| |
| private CTabItem tabItemList; |
| |
| private CTabItem tabItemTree; |
| |
| private XmiTab xmiTab; |
| |
| private Section headerContainer; |
| |
| public ModelEditor(Composite composite, IEclipseContext context, IModelResource modelProvider, IProject project, |
| final IResourcePool resourcePool) { |
| this.resourcePool = resourcePool; |
| this.modelProvider = modelProvider; |
| this.project = project; |
| this.context = context; |
| this.context.set(ModelEditor.class, this); |
| obsManager = new ObservablesManager(); |
| if (project != null) { |
| String localeString = ""; //$NON-NLS-1$ |
| final Object object = this.context.get(TranslationService.LOCALE); |
| // TODO simplify once we break e4 tools |
| // compatibility with Luna, in Luna we representation has changed to |
| // be Locale instead of String |
| if (object != null) { |
| localeString = object.toString(); |
| } |
| final ProjectOSGiTranslationProvider translationProvider = new ProjectOSGiTranslationProvider(project, |
| localeString) { |
| @Override |
| protected void updateResourceBundle() { |
| super.updateResourceBundle(); |
| if (viewer != null) { |
| viewer.getControl().getDisplay().asyncExec(() -> viewer.refresh()); |
| } |
| } |
| }; |
| context.set(ProjectOSGiTranslationProvider.class, translationProvider); |
| } |
| labelFeaturePaths.add(FeaturePath.fromList(UiPackageImpl.Literals.UI_ELEMENT__TO_BE_RENDERED)); |
| labelFeaturePaths.add(FeaturePath.fromList(UiPackageImpl.Literals.UI_ELEMENT__VISIBLE)); |
| |
| // This is a workaround until Bug 437207 is merged. |
| // @PersistState will not be invoked. |
| composite.addDisposeListener(arg0 -> persistState()); |
| } |
| |
| @PersistState |
| protected void persistState() { |
| if (listTab != null) { |
| listTab.saveSettings(); |
| } |
| } |
| |
| @PostConstruct |
| void postCreate(Composite composite) { |
| if (project == null) { |
| keyListener = event -> { |
| if ((event.stateMask & SWT.ALT) == SWT.ALT) { |
| findAndHighlight(context.get(Display.class).getFocusControl()); |
| } |
| }; |
| context.get(Display.class).addFilter(SWT.MouseUp, keyListener); |
| } |
| |
| context.set(ModelEditor.class, this); |
| context.set(IResourcePool.class, resourcePool); |
| context.set(EditingDomain.class, modelProvider.getEditingDomain()); |
| context.set(IModelResource.class, modelProvider); |
| |
| if (project != null) { |
| context.set(IProject.class, project); |
| } |
| |
| loadEditorFeatures(); |
| registerDefaultEditors(); |
| registerVirtualEditors(); |
| |
| registerContributedEditors(); |
| registerContributedVirtualEditors(); |
| registerContributedEditorTabs(); |
| loadContributionCreators(); |
| |
| fragment = modelProvider.getRoot().get(0) instanceof MModelFragments; |
| |
| // For Bug 396902, create this before creating the Form tab |
| emfDocumentProvider = new EMFDocumentResourceMediator(modelProvider); |
| |
| editorTabFolder = new CTabFolder(composite, SWT.BOTTOM); |
| tabItemTree = new CTabItem(editorTabFolder, SWT.NONE); |
| tabItemTree.setText(messages.ModelEditor_Form); |
| tabItemTree.setControl(createFormTab(editorTabFolder)); |
| tabItemTree.setImage(resourcePool.getImageUnchecked(ResourceProvider.IMG_Obj16_application_form)); |
| |
| tabListShow(true); |
| |
| tabItemXmi = new CTabItem(editorTabFolder, SWT.NONE); |
| tabItemXmi.setText(messages.ModelEditor_XMI); |
| xmiTab = createXMITab(editorTabFolder); |
| tabItemXmi.setControl(xmiTab); |
| tabItemXmi.setImage(resourcePool.getImageUnchecked(ResourceProvider.IMG_Obj16_chart_organisation)); |
| editorTabFolder.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| if (editorTabFolder.getSelectionIndex() == getTabIndex(tabItemXmi)) { |
| emfDocumentProvider.updateFromEMF(); |
| } |
| } |
| }); |
| |
| editorTabFolder.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| // When the list tab is visible, register the IViewEObjects |
| // interface |
| // This allows external commands to interact with the view. |
| // Eventually, all 3 tabs, or even the ModelEditor itself, could |
| // implement the interface. |
| final String key = "org.eclipse.e4.tools.active-object-viewer"; //$NON-NLS-1$ |
| if (listTab != null && editorTabFolder.getSelectionIndex() == getTabIndex(listTab.getTabItem())) { |
| app.getContext().set(key, listTab); |
| } else { |
| app.getContext().set(key, null); |
| } |
| } |
| }); |
| |
| editorTabFolder.setSelection(0); |
| } |
| |
| /** |
| * @param tabItem |
| * @return The index of the tab item. Should never return -1. |
| */ |
| static public int getTabIndex(CTabItem tabItem) { |
| return Arrays.asList(tabItem.getParent().getItems()).indexOf(tabItem); |
| } |
| |
| private void findAndHighlight(Control control) { |
| if (control != null) { |
| MApplicationElement m = findModelElement(control); |
| final MApplicationElement o = m; |
| if (m != null) { |
| final List<MApplicationElement> l = new ArrayList<>(); |
| do { |
| l.add(m); |
| m = (MApplicationElement) ((EObject) m).eContainer(); |
| } while (m != null); |
| |
| if (o instanceof MPart) { |
| System.err.println(getClass().getName() + ".findAndHighLight: " + o); //$NON-NLS-1$ |
| System.err |
| .println(getClass().getName() + ".findAndHighLight: " + ((EObject) o).eContainingFeature()); //$NON-NLS-1$ |
| } |
| |
| viewer.setSelection(new StructuredSelection(o)); |
| } |
| } |
| } |
| |
| private MApplicationElement findModelElement(Control control) { |
| do { |
| if (control.getData("modelElement") != null) { //$NON-NLS-1$ |
| return (MApplicationElement) control.getData("modelElement"); //$NON-NLS-1$ |
| } |
| control = control.getParent(); |
| } while (control != null); |
| |
| return null; |
| } |
| |
| private XmiTab createXMITab(Composite composite) { |
| final IEclipseContext childContext = context.createChild(); |
| childContext.set(Composite.class, composite); |
| childContext.set(EMFDocumentResourceMediator.class, emfDocumentProvider); |
| childContext.set(IEclipsePreferences.class, preferences); |
| childContext.set(IResourcePool.class, resourcePool); |
| final XmiTab ret = ContextInjectionFactory.make(XmiTab.class, childContext); |
| return ret; |
| } |
| |
| private Composite createFormTab(Composite composite) { |
| SashForm form = new SashForm(composite, SWT.HORIZONTAL); |
| |
| viewer = createTreeViewerArea(form); |
| |
| FormToolkit toolkit = new FormToolkit(form.getDisplay()); |
| |
| headerContainer = toolkit.createSection(form, Section.TITLE_BAR); |
| headerContainer.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL)); |
| |
| // Composite for storing the data |
| Composite contentContainer = toolkit.createComposite(headerContainer, SWT.NONE); |
| headerContainer.setClient(contentContainer); |
| |
| StackLayout layout = new StackLayout(); |
| contentContainer.setLayout(layout); |
| |
| form.setWeights(2, 5); |
| |
| viewer.getTree().addKeyListener(new KeyAdapter() { |
| @Override |
| public void keyReleased(final KeyEvent e) { |
| if (e.keyCode == SWT.DEL) { |
| final List<EObject> list = new ArrayList<>(); |
| final IStructuredSelection selection = (IStructuredSelection) viewer.getSelection(); |
| for (final Object o : ((StructuredSelection) selection).toList()) { |
| if (o instanceof EObject) { |
| list.add((EObject) o); |
| } |
| } |
| if (list.isEmpty() == false) { |
| final Command cmd = DeleteCommand.create(modelProvider.getEditingDomain(), list); |
| if (cmd.canExecute()) { |
| modelProvider.getEditingDomain().getCommandStack().execute(cmd); |
| } |
| } |
| } |
| } |
| }); |
| viewer.addSelectionChangedListener(event -> { |
| if (!event.getSelection().isEmpty()) { |
| final IStructuredSelection s = (IStructuredSelection) event.getSelection(); |
| if (s.getFirstElement() instanceof EObject) { |
| final EObject obj = (EObject) s.getFirstElement(); |
| final AbstractComponentEditor<?> editor1 = getEditor(obj.eClass()); |
| if (editor1 != null) { |
| currentEditor = editor1; |
| headerContainer.setText(editor1.getLabel(obj)); |
| obsManager.runAndCollect(() -> { |
| final Composite comp = editor1.getEditor(contentContainer, s.getFirstElement()); |
| layout.topControl = comp; |
| contentContainer.layout(true); |
| }); |
| } |
| } else { |
| final VirtualEntry<?, ?> entry = (VirtualEntry<?, ?>) s.getFirstElement(); |
| final AbstractComponentEditor<?> editor2 = getEditor(entry.getId()); |
| if (editor2 != null) { |
| currentEditor = editor2; |
| headerContainer.setText(editor2.getLabel(entry)); |
| obsManager.runAndCollect(() -> { |
| final Composite comp = editor2.getEditor(contentContainer, s.getFirstElement()); |
| layout.topControl = comp; |
| contentContainer.layout(true); |
| }); |
| } |
| } |
| |
| if (selectionService != null) { |
| selectionService.setSelection(s.getFirstElement()); |
| } |
| } |
| }); |
| |
| final MenuManager mgr = new MenuManager(); |
| mgr.setRemoveAllWhenShown(true); |
| mgr.addMenuListener(manager -> { |
| final IStructuredSelection s = (IStructuredSelection) viewer.getSelection(); |
| final List<?> listOfSelections = s.toList(); |
| final int noSelected = listOfSelections.size(); |
| |
| boolean addSeparator = false; |
| // single selection |
| if (!s.isEmpty() && noSelected == 1) { |
| List<Action> actions; |
| if (s.getFirstElement() instanceof VirtualEntry) { |
| actions = getEditor(((VirtualEntry<?, ?>) s.getFirstElement()).getId()) |
| .getActions(s.getFirstElement()); |
| if (actions.size() > 0) { |
| final MenuManager addMenu1 = new MenuManager(messages.ModelEditor_AddChild); |
| for (final Action a1 : actions) { |
| addSeparator = true; |
| addMenu1.add(a1); |
| } |
| manager.add(addMenu1); |
| } |
| |
| actions = getEditor(((VirtualEntry<?, ?>) s.getFirstElement()).getId()) |
| .getActionsImport(s.getFirstElement()); |
| if (actions.size() > 0) { |
| final MenuManager menu1 = new MenuManager(messages.ModelEditor_Import3x); |
| for (final Action a2 : actions) { |
| addSeparator = true; |
| menu1.add(a2); |
| } |
| manager.add(menu1); |
| } |
| |
| if (addSeparator) { |
| manager.add(new Separator()); |
| } |
| |
| } else { |
| |
| final EObject o = (EObject) s.getFirstElement(); |
| final AbstractComponentEditor<?> editor = getEditor(o.eClass()); |
| |
| // Build Add Child menu |
| if (editor != null) { |
| actions = new ArrayList<>(editor.getActions(s.getFirstElement())); |
| } else { |
| actions = new ArrayList<>(); |
| } |
| |
| if (actions.size() > 0) { |
| final MenuManager addMenu2 = new MenuManager(messages.ModelEditor_AddChild); |
| for (final Action a3 : actions) { |
| addSeparator = true; |
| addMenu2.add(a3); |
| } |
| manager.add(addMenu2); |
| } |
| |
| // Build import menu |
| if (editor != null) { |
| actions = new ArrayList<>(editor.getActionsImport(s.getFirstElement())); |
| } else { |
| actions = new ArrayList<>(); |
| } |
| |
| if (actions.size() > 0) { |
| // TODO WIM - extract nls |
| final MenuManager menu2 = new MenuManager(messages.ModelEditor_Import3x); |
| for (final Action a4 : actions) { |
| addSeparator = true; |
| menu2.add(a4); |
| } |
| manager.add(menu2); |
| } |
| |
| } |
| } |
| |
| // single & multi selection |
| if (noSelected > 0) { |
| |
| // add delete entry if there are no virtual entries in selection all selected |
| // elements have a container |
| if (listOfSelections.stream().noneMatch(VirtualEntry.class::isInstance) |
| && listOfSelections.stream().filter(EObject.class::isInstance).map(EObject.class::cast) |
| .map(EObject::eContainer).allMatch(Objects::nonNull)) { |
| addSeparator = true; |
| manager.add(new Action(messages.ModelEditor_Delete, ImageDescriptor |
| .createFromImage(resourcePool.getImageUnchecked(ResourceProvider.IMG_Obj16_cross))) { |
| @Override |
| public void run() { |
| final CompoundCommand cmd = new CompoundCommand(); |
| EditingDomain editingDomain = modelProvider.getEditingDomain(); |
| listOfSelections.forEach(o -> cmd.append(DeleteCommand.create(editingDomain, o))); |
| if (cmd.canExecute() && !cmd.isEmpty()) { |
| editingDomain.getCommandStack().execute(cmd); |
| } |
| } |
| }); |
| } |
| |
| if (!isModelFragment() && modelExtractor != null) { |
| manager.add(new Action(messages.ModelEditor_ExtractFragment, ImageDescriptor |
| .createFromImage(resourcePool.getImageUnchecked(ResourceProvider.IMG_ModelFragments))) { |
| @Override |
| public void run() { |
| final ArrayList<MApplicationElement> maes = new ArrayList<>(); |
| for (final Object objSelect : listOfSelections) { |
| EObject container = null; |
| if (objSelect instanceof VirtualEntry) { |
| |
| @SuppressWarnings("unchecked") |
| final VirtualEntry<EObject, MApplicationElement> ve = (VirtualEntry<EObject, MApplicationElement>) objSelect; |
| container = ve.getOriginalParent(); |
| final IObservableList<MApplicationElement> list = ve.getList(); |
| final Iterator<MApplicationElement> iterator = list.iterator(); |
| while (iterator.hasNext()) { |
| maes.add(iterator.next()); |
| } |
| |
| } else { |
| container = ((EObject) objSelect).eContainer(); |
| final MApplicationElement objSelect2 = (MApplicationElement) objSelect; |
| if (!(objSelect2 instanceof MApplication)) { |
| maes.add(objSelect2); |
| } else { |
| // can't extract application |
| return; |
| } |
| |
| } |
| |
| final String containerId = ((MApplicationElement) container).getElementId(); |
| if (containerId == null || containerId.length() == 0) { |
| MessageDialog.openError(viewer.getControl().getShell(), null, |
| messages.ModelEditor_ExtractFragment_NoParentId); |
| |
| return; |
| } |
| |
| } |
| |
| if (modelExtractor.extract(viewer.getControl().getShell(), project, maes)) { |
| final Command cmd = DeleteCommand.create(modelProvider.getEditingDomain(), maes); |
| if (cmd.canExecute()) { |
| modelProvider.getEditingDomain().getCommandStack().execute(cmd); |
| } |
| } |
| |
| } |
| }); |
| } |
| |
| |
| } |
| |
| final IExtensionRegistry registry = RegistryFactory.getRegistry(); |
| final IExtensionPoint extPoint = registry.getExtensionPoint("org.eclipse.e4.tools.emf.ui.scripting"); //$NON-NLS-1$ |
| final IConfigurationElement[] elements = extPoint.getConfigurationElements(); |
| |
| if (elements.length > 0 && !s.isEmpty() && s.getFirstElement() instanceof MApplicationElement |
| && noSelected == 1) { |
| if (addSeparator) { |
| manager.add(new Separator()); |
| } |
| |
| addSeparator = false; |
| |
| final MenuManager scriptExecute = new MenuManager(messages.ModelEditor_Script); |
| manager.add(scriptExecute); |
| for (final IConfigurationElement e : elements) { |
| final IConfigurationElement le = e; |
| scriptExecute.add(new Action(e.getAttribute("label")) { //$NON-NLS-1$ |
| @Override |
| public void run() { |
| try { |
| final MApplicationElement o = (MApplicationElement) s.getFirstElement(); |
| final IScriptingSupport support = (IScriptingSupport) le |
| .createExecutableExtension("class"); //$NON-NLS-1$ |
| IEclipseContext ctx = null; |
| if (project == null) { |
| if (o instanceof MContext) { |
| ctx = ((MContext) o).getContext(); |
| } else { |
| ctx = ModelUtils.getContainingContext(o); |
| } |
| } |
| |
| support.openEditor(viewer.getControl().getShell(), s.getFirstElement(), ctx); |
| } catch (CoreException e) { |
| logger.warn(e); |
| } |
| } |
| }); |
| } |
| } |
| |
| if (project != null) { |
| |
| if (addSeparator) { |
| manager.add(new Separator()); |
| } |
| |
| final Action nlsAction = new Action(messages.ModelEditor_ExternalizeStrings) { |
| @Override |
| public void run() { |
| final ExternalizeStringHandler h = ContextInjectionFactory.make(ExternalizeStringHandler.class, |
| context); |
| ContextInjectionFactory.invoke(h, Execute.class, context); |
| } |
| }; |
| |
| final Action extIdAction = new Action(messages.ModelEditor_ExportIds) { |
| @Override |
| public void run() { |
| final ExportIdsHandler h = ContextInjectionFactory.make(ExportIdsHandler.class, context); |
| ContextInjectionFactory.invoke(h, Execute.class, context); |
| } |
| }; |
| |
| manager.add(nlsAction); |
| manager.add(extIdAction); |
| } else { |
| if (addSeparator) { |
| manager.add(new Separator()); |
| } |
| |
| if (s.getFirstElement() instanceof MUIElement) { |
| final MUIElement el1 = (MUIElement) s.getFirstElement(); |
| if (el1.getWidget() instanceof Control) { |
| manager.add(new Action(messages.ModelEditor_ShowControl) { |
| |
| @Override |
| public void run() { |
| ControlHighlighter.show((Control) el1.getWidget()); |
| } |
| }); |
| |
| } |
| } |
| |
| } |
| |
| if (addSeparator) { |
| manager.add(new Separator()); |
| } |
| |
| final Action expandAction = new Action(messages.ModelEditor_ExpandSubtree) { |
| @Override |
| public void run() { |
| if (!s.isEmpty()) { |
| if (viewer.getExpandedState(s.getFirstElement())) { |
| viewer.collapseToLevel(s.getFirstElement(), AbstractTreeViewer.ALL_LEVELS); |
| } else { |
| viewer.expandToLevel(s.getFirstElement(), AbstractTreeViewer.ALL_LEVELS); |
| } |
| } |
| } |
| }; |
| |
| manager.add(expandAction); |
| |
| if (s.getFirstElement() instanceof EObject) { |
| manager.add(new Separator()); |
| final EObject el2 = (EObject) s.getFirstElement(); |
| final Action gotoXmiAction = new Action(messages.ModelEditor_goto_xmi) { |
| @Override |
| public void run() { |
| gotoEObject(TAB_XMI, el2); |
| } |
| }; |
| manager.add(gotoXmiAction); |
| |
| if (listTab != null) { |
| if (EmfUtil.getAttribute(el2, "elementId") != null) { //$NON-NLS-1$ |
| final Action gotoListAction = new Action(messages.ModelEditor_goto_list) { |
| @Override |
| public void run() { |
| gotoEObject(TAB_LIST, el2); |
| } |
| }; |
| manager.add(gotoListAction); |
| } |
| } |
| |
| } |
| }); |
| |
| // Save the stateMask |
| viewer.getTree().addKeyListener(new KeyListener() { |
| @Override |
| public void keyReleased(KeyEvent e) { |
| if (mod1Down && (e.keyCode & SWT.MOD1) == SWT.MOD1) { |
| mod1Down = false; |
| } |
| } |
| |
| @Override |
| public void keyPressed(KeyEvent e) { |
| if (!mod1Down && (e.keyCode & SWT.MOD1) == SWT.MOD1) { |
| mod1Down = true; |
| } |
| } |
| }); |
| |
| viewer.addTreeListener(new ITreeViewerListener() { |
| @Override |
| public void treeExpanded(final TreeExpansionEvent event) { |
| if (mod1Down) { |
| viewer.getTree().getDisplay() |
| .asyncExec(() -> viewer.expandToLevel(event.getElement(), AbstractTreeViewer.ALL_LEVELS)); |
| } |
| } |
| |
| @Override |
| public void treeCollapsed(final TreeExpansionEvent event) { |
| if (mod1Down) { |
| viewer.getTree().getDisplay() |
| .asyncExec(() -> viewer.collapseToLevel(event.getElement(), AbstractTreeViewer.ALL_LEVELS)); |
| } |
| } |
| }); |
| |
| viewer.getControl().setMenu(mgr.createContextMenu(viewer.getControl())); |
| viewer.setSelection(new StructuredSelection(modelProvider.getRoot())); |
| |
| return form; |
| } |
| |
| public IExtensionLookup getExtensionLookup() { |
| return extensionLookup; |
| } |
| |
| public boolean isAutoCreateElementId() { |
| return autoCreateElementId && project != null; |
| } |
| |
| public IProject getProject() { |
| return project; |
| } |
| |
| public ImageRegistry getComponentImages() { |
| return componentImages; |
| } |
| |
| public boolean isShowXMIId() { |
| return showXMIId; |
| } |
| |
| private void loadContributionCreators() { |
| final IExtensionRegistry registry = RegistryFactory.getRegistry(); |
| final IExtensionPoint extPoint = registry.getExtensionPoint("org.eclipse.e4.tools.emf.ui.editors"); //$NON-NLS-1$ |
| |
| for (final IConfigurationElement el : extPoint.getConfigurationElements()) { |
| if (!"contributionClassCreator".equals(el.getName())) { //$NON-NLS-1$ |
| continue; |
| } |
| |
| try { |
| contributionCreator.add((IContributionClassCreator) el.createExecutableExtension("class")); //$NON-NLS-1$ |
| } catch (final CoreException e) { |
| logger.warn(e); |
| } |
| } |
| } |
| |
| public IContributionClassCreator getContributionCreator(EClass eClass) { |
| for (final IContributionClassCreator c : contributionCreator) { |
| if (c.isSupported(eClass)) { |
| return c; |
| } |
| } |
| return null; |
| } |
| |
| private void loadEditorFeatures() { |
| final IExtensionRegistry registry = RegistryFactory.getRegistry(); |
| final IExtensionPoint extPoint = registry.getExtensionPoint("org.eclipse.e4.tools.emf.ui.editors"); //$NON-NLS-1$ |
| |
| for (final IConfigurationElement el : extPoint.getConfigurationElements()) { |
| if (!"editorfeature".equals(el.getName())) { //$NON-NLS-1$ |
| continue; |
| } |
| |
| try { |
| editorFeatures.add((IEditorFeature) el.createExecutableExtension("class")); //$NON-NLS-1$ |
| } catch (final CoreException e) { |
| logger.warn(e); |
| } |
| } |
| } |
| |
| public boolean isModelFragment() { |
| return fragment; |
| } |
| |
| public boolean isLiveModel() { |
| return !modelProvider.isSaveable(); |
| } |
| |
| public List<FeatureClass> getFeatureClasses(EClass eClass, EStructuralFeature feature) { |
| final List<FeatureClass> list = new ArrayList<>(); |
| |
| for (final IEditorFeature f : editorFeatures) { |
| list.addAll(f.getFeatureClasses(eClass, feature)); |
| } |
| |
| return list; |
| } |
| |
| private TreeViewer createTreeViewerArea(Composite parent) { |
| |
| final Composite treeArea = new Composite(parent, SWT.BORDER); |
| |
| treeArea.setLayout(new FillLayout()); |
| treeArea.setData(CSS_CLASS_KEY, "formContainer"); //$NON-NLS-1$ |
| treeArea.setBackgroundMode(SWT.INHERIT_DEFAULT); |
| |
| TreeViewer tempViewer = null; |
| final String property = System |
| .getProperty(ORG_ECLIPSE_E4_TOOLS_MODELEDITOR_FILTEREDTREE_ENABLED_XMITAB_DISABLED); |
| if (property != null || preferences.getBoolean(ModelEditorPreferences.TAB_FORM_SEARCH_SHOW, false)) { |
| final FilteredTree viewParent = new FilteredTree(treeArea, |
| SWT.MULTI | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL, new PatternFilter(true)); |
| tempViewer = viewParent.getViewer(); |
| } else { |
| tempViewer = new TreeViewerEx(treeArea, SWT.MULTI | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL, |
| emfDocumentProvider, modelProvider); |
| } |
| final TreeViewer viewer = tempViewer; |
| |
| final FontDescriptor italicFontDescriptor = FontDescriptor.createFrom(viewer.getControl().getFont()) |
| .setStyle(SWT.ITALIC); |
| viewer.setLabelProvider(new DelegatingStyledCellLabelProvider( |
| new ComponentLabelProvider(this, messages, italicFontDescriptor))); |
| final ObservableListTreeContentProvider<Object> contentProvider = new ObservableListTreeContentProvider<>( |
| new ObservableFactoryImpl(), new TreeStructureAdvisor<>() { |
| }); |
| viewer.setContentProvider(contentProvider); |
| |
| final WritableSet<EObject> clearedSet = new WritableSet<>(); |
| |
| contentProvider.getKnownElements().addSetChangeListener(event -> { |
| for (final Object o1 : event.diff.getAdditions()) { |
| if (o1 instanceof EObject) { |
| clearedSet.add((EObject) o1); |
| } |
| } |
| |
| for (final Object o2 : event.diff.getRemovals()) { |
| if (o2 instanceof EObject) { |
| clearedSet.remove(o2); |
| } |
| } |
| }); |
| |
| for (final FeaturePath p : labelFeaturePaths) { |
| @SuppressWarnings("unchecked") |
| final IObservableMap<EObject, Object> map = EMFProperties.value(p).observeDetail(clearedSet); |
| map.addMapChangeListener(event -> viewer.update(event.diff.getChangedKeys().toArray(), null)); |
| } |
| |
| viewer.setInput(modelProvider.getRoot()); |
| viewer.setAutoExpandLevel(2); |
| viewer.expandToLevel(viewer.getAutoExpandLevel()); |
| viewer.addDoubleClickListener(event -> { |
| final TreeViewer viewer1 = (TreeViewer) event.getViewer(); |
| final IStructuredSelection thisSelection = (IStructuredSelection) event.getSelection(); |
| final Object selectedNode = thisSelection.getFirstElement(); |
| if (mod1Down) { |
| if (viewer1.getExpandedState(selectedNode)) { |
| viewer1.setExpandedState(selectedNode, false); |
| } else { |
| viewer1.expandToLevel(selectedNode, AbstractTreeViewer.ALL_LEVELS); |
| } |
| } else { |
| viewer1.setExpandedState(selectedNode, !viewer1.getExpandedState(selectedNode)); |
| } |
| |
| }); |
| |
| // Effect of filtered tree implementation (bug 391086) |
| viewer.getTree().addTreeListener(new TreeAdapter() { |
| @Override |
| public void treeCollapsed(TreeEvent e) { |
| viewer.expandToLevel(viewer.getAutoExpandLevel()); |
| } |
| }); |
| |
| final int ops = DND.DROP_MOVE; |
| viewer.addDragSupport(ops, new Transfer[] { MemoryTransfer.getInstance() }, new DragListener(viewer)); |
| viewer.addDropSupport(ops, new Transfer[] { MemoryTransfer.getInstance() }, |
| new DropListener(viewer, modelProvider.getEditingDomain())); |
| |
| return viewer; |
| } |
| |
| private void registerContributedVirtualEditors() { |
| final IExtensionRegistry registry = RegistryFactory.getRegistry(); |
| final IExtensionPoint extPoint = registry.getExtensionPoint("org.eclipse.e4.tools.emf.ui.editors"); //$NON-NLS-1$ |
| |
| for (final IConfigurationElement el : extPoint.getConfigurationElements()) { |
| if (!"virtualeditor".equals(el.getName())) { //$NON-NLS-1$ |
| continue; |
| } |
| |
| final IContributionFactory fact = context.get(IContributionFactory.class); |
| final AbstractComponentEditor<?> editor = (AbstractComponentEditor<?>) fact |
| .create("bundleclass://" + el.getContributor().getName() + "/" + el.getAttribute("class"), context); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| registerEditor(el.getAttribute("id"), editor); //$NON-NLS-1$ |
| } |
| } |
| |
| private void registerVirtualEditors() { |
| registerEditor(VIRTUAL_PART_MENU, VPartMenuEditor.class); |
| registerEditor(VIRTUAL_PART_TRIMS, VPartTrimEditor.class); |
| registerEditor(VIRTUAL_HANDLER, VHandlerEditor.class); |
| registerEditor(VIRTUAL_CONTROLS, VControlsEditor.class); |
| registerEditor(VIRTUAL_BINDING_TABLE, VBindingTableEditor.class); |
| registerEditor(VIRTUAL_COMMAND, VCommandEditor.class); |
| registerEditor(VIRTUAL_APPLICATION_WINDOWS, VApplicationWindowEditor.class); |
| registerEditor(VIRTUAL_WINDOW_WINDOWS, VWindowWindowsEditor.class); |
| registerEditor(VIRTUAL_PERSPECTIVE_WINDOWS, VPerspectiveWindowsEditor.class); |
| registerEditor(VIRTUAL_WINDOW_CONTROLS, VWindowControlEditor.class); |
| registerEditor(VIRTUAL_WINDOW_SNIPPETS, VSnippetsEditor.class); |
| registerEditor(VIRTUAL_PART_DESCRIPTORS, VPartDescriptor.class); |
| registerEditor(VIRTUAL_PARTDESCRIPTOR_MENU, VPartDescriptorMenuEditor.class); |
| registerEditor(VIRTUAL_PARTDESCRIPTOR_TRIMS, VPartDescriptorTrimEditor.class); |
| registerEditor(VIRTUAL_TRIMMED_WINDOW_TRIMS, VWindowTrimEditor.class); |
| registerEditor(VIRTUAL_ADDONS, VApplicationAddons.class); |
| registerEditor(VIRTUAL_MENU_CONTRIBUTIONS, VMenuContributionsEditor.class); |
| registerEditor(VIRTUAL_TOOLBAR_CONTRIBUTIONS, VToolBarContributionsEditor.class); |
| registerEditor(VIRTUAL_TRIM_CONTRIBUTIONS, VTrimContributionsEditor.class); |
| registerEditor(VIRTUAL_WINDOW_SHARED_ELEMENTS, VWindowSharedElementsEditor.class); |
| registerEditor(VIRTUAL_MODEL_FRAGEMENTS, VModelFragmentsEditor.class); |
| registerEditor(VIRTUAL_MODEL_IMPORTS, VModelImportsEditor.class); |
| registerEditor(VIRTUAL_CATEGORIES, VApplicationCategoriesEditor.class); |
| registerEditor(VIRTUAL_PARAMETERS, VItemParametersEditor.class); |
| registerEditor(VIRTUAL_ROOT_CONTEXTS, VRootBindingContexts.class); |
| registerEditor(VIRTUAL_PERSPECTIVE_CONTROLS, VPerspectiveControlEditor.class); |
| registerEditor(VIRTUAL_PERSPECTIVE_TRIMS, VPerspectiveTrimEditor.class); |
| registerEditor(VIRTUAL_SNIPPETS, VSnippetsEditor.class); |
| } |
| |
| public void setSelection(Object element) { |
| viewer.setSelection(new StructuredSelection(element)); |
| } |
| |
| private void registerContributedEditors() { |
| final IExtensionRegistry registry = RegistryFactory.getRegistry(); |
| final IExtensionPoint extPoint = registry.getExtensionPoint("org.eclipse.e4.tools.emf.ui.editors"); //$NON-NLS-1$ |
| |
| for (final IConfigurationElement el : extPoint.getConfigurationElements()) { |
| if (!"editor".equals(el.getName())) { //$NON-NLS-1$ |
| continue; |
| } |
| |
| try { |
| final IEditorDescriptor desc = (IEditorDescriptor) el.createExecutableExtension("descriptorClass"); //$NON-NLS-1$ |
| final EClass eClass = desc.getEClass(); |
| final IContributionFactory fact = context.get(IContributionFactory.class); |
| final AbstractComponentEditor<?> editor = (AbstractComponentEditor<?>) fact.create( |
| "bundleclass://" + el.getContributor().getName() + "/" + desc.getEditorClass().getName(), //$NON-NLS-1$ //$NON-NLS-2$ |
| context); |
| registerEditor(eClass, editor); |
| } catch (final CoreException e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| private void registerContributedEditorTabs() { |
| final IExtensionRegistry registry = RegistryFactory.getRegistry(); |
| final IExtensionPoint extPoint = registry.getExtensionPoint("org.eclipse.e4.tools.emf.ui.editors"); //$NON-NLS-1$ |
| for (final IConfigurationElement el : extPoint.getConfigurationElements()) { |
| if (!"editorTab".equals(el.getName())) { //$NON-NLS-1$ |
| continue; |
| } |
| |
| try { |
| final Object o = el.createExecutableExtension("contribution"); //$NON-NLS-1$ |
| if (o instanceof AbstractElementEditorContribution) { |
| final AbstractElementEditorContribution contribution = (AbstractElementEditorContribution) o; |
| ContextInjectionFactory.inject(contribution, context); |
| final Class<?> contribElem = contribution.getContributableTo(); |
| if (contribElem == null) { |
| continue; |
| } |
| if (!tabContributions.containsKey(contribElem)) { |
| tabContributions.put(contribElem, new ArrayList<AbstractElementEditorContribution>()); |
| } |
| final List<AbstractElementEditorContribution> res = tabContributions.get(contribElem); |
| res.add(contribution); |
| } |
| } catch (final CoreException e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| public List<AbstractElementEditorContribution> getTabContributionsForClass(Class<?> clazz) { |
| final List<AbstractElementEditorContribution> ret = new ArrayList<>(); |
| for (Map.Entry<Class<?>, List<AbstractElementEditorContribution>> entry : tabContributions.entrySet()) { |
| Class<?> clasz = entry.getKey(); |
| if (clasz.isAssignableFrom(clazz)) { |
| ret.addAll(entry.getValue()); |
| } |
| } |
| return ret; |
| } |
| |
| private void registerDefaultEditors() { |
| |
| registerEditor(ApplicationPackageImpl.Literals.APPLICATION, ApplicationEditor.class); |
| registerEditor(ApplicationPackageImpl.Literals.ADDON, AddonsEditor.class); |
| |
| registerEditor(CommandsPackageImpl.Literals.KEY_BINDING, KeyBindingEditor.class); |
| registerEditor(CommandsPackageImpl.Literals.HANDLER, HandlerEditor.class); |
| registerEditor(CommandsPackageImpl.Literals.COMMAND, CommandEditor.class); |
| registerEditor(CommandsPackageImpl.Literals.COMMAND_PARAMETER, CommandParameterEditor.class); |
| registerEditor(CommandsPackageImpl.Literals.PARAMETER, ParameterEditor.class); |
| registerEditor(CommandsPackageImpl.Literals.BINDING_TABLE, BindingTableEditor.class); |
| registerEditor(CommandsPackageImpl.Literals.BINDING_CONTEXT, BindingContextEditor.class); |
| registerEditor(CommandsPackageImpl.Literals.CATEGORY, CategoryEditor.class); |
| |
| registerEditor(MenuPackageImpl.Literals.TOOL_BAR, ToolBarEditor.class); |
| registerEditor(MenuPackageImpl.Literals.DIRECT_TOOL_ITEM, DirectToolItemEditor.class); |
| registerEditor(MenuPackageImpl.Literals.HANDLED_TOOL_ITEM, HandledToolItemEditor.class); |
| registerEditor(MenuPackageImpl.Literals.TOOL_BAR_SEPARATOR, ToolBarSeparatorEditor.class); |
| registerEditor(MenuPackageImpl.Literals.TOOL_CONTROL, ToolControlEditor.class); |
| registerEditor(MenuPackageImpl.Literals.MENU, MenuEditor.class); |
| registerEditor(MenuPackageImpl.Literals.POPUP_MENU, PopupMenuEditor.class); |
| registerEditor(MenuPackageImpl.Literals.MENU_SEPARATOR, MenuSeparatorEditor.class); |
| registerEditor(MenuPackageImpl.Literals.HANDLED_MENU_ITEM, HandledMenuItemEditor.class); |
| registerEditor(MenuPackageImpl.Literals.DIRECT_MENU_ITEM, DirectMenuItemEditor.class); |
| registerEditor(MenuPackageImpl.Literals.MENU_CONTRIBUTION, MenuContributionEditor.class); |
| registerEditor(MenuPackageImpl.Literals.TOOL_BAR_CONTRIBUTION, ToolBarContributionEditor.class); |
| registerEditor(MenuPackageImpl.Literals.TRIM_CONTRIBUTION, TrimContributionEditor.class); |
| registerEditor(MenuPackageImpl.Literals.DYNAMIC_MENU_CONTRIBUTION, DynamicMenuContributionEditor.class); |
| |
| registerEditor(UiPackageImpl.Literals.CORE_EXPRESSION, CoreExpressionEditor.class); |
| registerEditor(UiPackageImpl.Literals.IMPERATIVE_EXPRESSION, ImperativeExpressionEditor.class); |
| |
| registerEditor(BasicPackageImpl.Literals.COMPOSITE_PART, CompositePartEditor.class); |
| registerEditor(BasicPackageImpl.Literals.PART, PartEditor.class); |
| registerEditor(BasicPackageImpl.Literals.WINDOW, WindowEditor.class); |
| registerEditor(BasicPackageImpl.Literals.TRIMMED_WINDOW, TrimmedWindowEditor.class); |
| registerEditor(BasicPackageImpl.Literals.PART_SASH_CONTAINER, PartSashContainerEditor.class); |
| registerEditor(AdvancedPackageImpl.Literals.AREA, AreaEditor.class); |
| registerEditor(BasicPackageImpl.Literals.PART_STACK, PartStackEditor.class); |
| registerEditor(BasicPackageImpl.Literals.TRIM_BAR, TrimBarEditor.class); |
| |
| registerEditor( |
| org.eclipse.e4.ui.model.application.descriptor.basic.impl.BasicPackageImpl.Literals.PART_DESCRIPTOR, |
| PartDescriptorEditor.class); |
| |
| registerEditor(AdvancedPackageImpl.Literals.PERSPECTIVE_STACK, PerspectiveStackEditor.class); |
| registerEditor(AdvancedPackageImpl.Literals.PERSPECTIVE, PerspectiveEditor.class); |
| registerEditor(AdvancedPackageImpl.Literals.PLACEHOLDER, PlaceholderEditor.class); |
| |
| registerEditor(FragmentPackageImpl.Literals.MODEL_FRAGMENTS, ModelFragmentsEditor.class); |
| registerEditor(FragmentPackageImpl.Literals.STRING_MODEL_FRAGMENT, StringModelFragment.class); |
| } |
| |
| public void tabListShow(Boolean show) { |
| if (editorTabFolder == null) { |
| return; |
| } |
| if (show == false) { |
| if (listTab != null) { |
| // remove the tab from the folder |
| listTab.getTabItem().dispose(); |
| ContextInjectionFactory.uninject(listTab, listTab.getContext()); |
| listTab = null; |
| } |
| } else { |
| if (listTab == null) { |
| final IEclipseContext child = context.createChild(); |
| child.set(CTabFolder.class, editorTabFolder); |
| child.set(EMFDocumentResourceMediator.class, emfDocumentProvider); |
| child.set(IGotoObject.class, this); |
| child.set(Messages.class, messages); |
| listTab = ContextInjectionFactory.make(ListTab.class, child); |
| tabItemList = listTab.getTabItem(); |
| } |
| } |
| } |
| |
| @Inject |
| public void setNotVisibleColor(@Preference(ModelEditorPreferences.NOT_VISIBLE_COLOR) String prefColorText) { |
| final RGB current = JFaceResources.getColorRegistry().getRGB(ComponentLabelProvider.NOT_VISIBLE_KEY); |
| RGB prefColor = StringConverter.asRGB(prefColorText, new RGB(200, 200, 200)); |
| |
| if (current == null || !current.equals(prefColor)) { |
| JFaceResources.getColorRegistry().put(ComponentLabelProvider.NOT_VISIBLE_KEY, prefColor); |
| } |
| |
| if (viewer != null) { |
| viewer.refresh(); |
| viewer.getControl().redraw(); |
| } |
| } |
| |
| @Inject |
| public void setNotRenderedColor(@Preference(ModelEditorPreferences.NOT_RENDERED_COLOR) String prefColorText) { |
| RGB prefColor = StringConverter.asRGB(prefColorText, new RGB(200, 200, 200)); |
| final RGB current = JFaceResources.getColorRegistry().getRGB(ComponentLabelProvider.NOT_RENDERED_KEY); |
| |
| if (current == null || !current.equals(prefColor)) { |
| JFaceResources.getColorRegistry().put(ComponentLabelProvider.NOT_RENDERED_KEY, prefColor); |
| } |
| |
| if (viewer != null) { |
| viewer.refresh(); |
| viewer.getControl().redraw(); |
| } |
| } |
| |
| @Inject |
| public void setNotVisibleRenderedColor( |
| @Preference(ModelEditorPreferences.NOT_VISIBLE_AND_RENDERED_COLOR) String prefColorText) { |
| RGB prefColor = StringConverter.asRGB(prefColorText, new RGB(200, 200, 200)); |
| final RGB current = JFaceResources.getColorRegistry() |
| .getRGB(ComponentLabelProvider.NOT_VISIBLE_AND_RENDERED_KEY); |
| |
| if (current == null || !current.equals(prefColor)) { |
| JFaceResources.getColorRegistry().put(ComponentLabelProvider.NOT_VISIBLE_AND_RENDERED_KEY, prefColor); |
| } |
| |
| if (viewer != null) { |
| viewer.refresh(); |
| viewer.getControl().redraw(); |
| } |
| } |
| |
| private void registerEditor(EClass eClass, Class<? extends AbstractComponentEditor<?>> clazz) { |
| registerEditor(eClass.getInstanceClassName(), clazz); |
| } |
| |
| /** |
| * Register a class to use to create an editor for a given key |
| * |
| * @param ley |
| * @param clazz |
| */ |
| private void registerEditor(String key, Class<? extends AbstractComponentEditor<?>> clazz) { |
| editorsClasses.put(key, clazz); |
| } |
| |
| /** |
| * Register directly a created editor for a given key |
| * |
| * @param instanceClassName |
| * @param clazz |
| */ |
| private void registerEditor(String key, AbstractComponentEditor<?> editor) { |
| editors.put(key, editor); |
| } |
| |
| /** |
| * Get editor from an eClass. May return the registered editor for this eclass, |
| * or the editor for a parent EClass or the default editor |
| * |
| * @param eClass the eClass to get editor for |
| * @return the {@link AbstractComponentEditor} found (never null). |
| */ |
| public AbstractComponentEditor<?> getEditor(EClass eClass) { |
| AbstractComponentEditor<?> editor = getEditor(eClass.getInstanceClassName(), false); |
| |
| if (editor == null) { |
| // May be can try to use the ancestor editor if not found or the default editor |
| for (final EClass cl : eClass.getESuperTypes()) { |
| editor = getEditor(cl); |
| if (editor != null) { |
| break; |
| } |
| } |
| |
| // Editor is still null ? , will use the default editor |
| if (editor == null) { |
| editor = ContextInjectionFactory.make(DefaultEditor.class, context); |
| } |
| |
| // register the parent or default editor |
| editors.put(eClass.getInstanceClassName(), editor); |
| } |
| |
| return editor; |
| |
| } |
| |
| public AbstractComponentEditor<?> getEditor(String key) { |
| return getEditor(key, true); |
| } |
| |
| /** |
| * get editor from a string key. |
| * |
| * @param key : the editor string key |
| * @param createDefaultIfNull if true, returns the default editor if no editor |
| * found |
| * @return the {@link AbstractComponentEditor} if exists. Never null if |
| * createDefaultIfNull is true |
| */ |
| private AbstractComponentEditor<?> getEditor(String key, boolean createDefaultIfNull) { |
| AbstractComponentEditor<?> editor = editors.get(key); |
| |
| if (editor == null) { |
| |
| // Editor not yet created in the map... must create instance using registered |
| // class |
| Class<? extends AbstractComponentEditor<?>> cz = editorsClasses.get(key); |
| if (cz != null) { |
| editor = ContextInjectionFactory.make(cz, context); |
| editors.put(key, editor); |
| |
| // Then manage the feature maps... |
| manageFeatureMap(editor); |
| } |
| |
| } |
| |
| return editor; |
| } |
| |
| private void manageFeatureMap(AbstractComponentEditor<?> editor) { |
| for (final FeaturePath p : editor.getLabelProperties()) { |
| boolean found = false; |
| for (final FeaturePath tmp : labelFeaturePaths) { |
| if (equalsPaths(p, tmp)) { |
| found = true; |
| break; |
| } |
| } |
| |
| if (!found) { |
| labelFeaturePaths.add(p); |
| } |
| } |
| |
| } |
| |
| public void registerEditor(EClass eClass, AbstractComponentEditor<?> editor) { |
| editors.put(eClass.getInstanceClassName(), editor); |
| manageFeatureMap(editor); |
| } |
| |
| private boolean equalsPaths(FeaturePath p1, FeaturePath p2) { |
| if (p1.getFeaturePath().length == p2.getFeaturePath().length) { |
| for (int i = 0; i < p1.getFeaturePath().length; i++) { |
| if (!p1.getFeaturePath()[i].equals(p2.getFeaturePath()[i])) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| @Persist |
| public void doSave(@Optional IProgressMonitor monitor) { |
| |
| try { |
| setSaving(true); |
| if (modelProvider.isSaveable()) { |
| modelProvider.save(); |
| } |
| } finally { |
| setSaving(false); |
| } |
| } |
| |
| private void setSaving(boolean saving) { |
| this.saving = saving; |
| } |
| |
| /** |
| * @return true if the editor is currently in the progress of saving. |
| */ |
| protected boolean isSaving() { |
| return saving; |
| } |
| |
| @Focus |
| public void setFocus() { |
| if (clipboardHandler == null) { |
| clipboardHandler = new ClipboardHandler(); |
| } |
| if (clipboardService != null) { |
| clipboardService.setHandler(clipboardHandler); |
| } |
| } |
| |
| public void setHeaderTitle(String title) { |
| headerContainer.setText(title); |
| } |
| |
| @PreDestroy |
| void dispose() { |
| try { |
| obsManager.dispose(); |
| } catch (final Exception e) { |
| e.printStackTrace(); |
| } |
| |
| if (project == null) { |
| context.get(Display.class).removeFilter(SWT.MouseUp, keyListener); |
| } |
| if (xmiTab != null) { |
| ContextInjectionFactory.uninject(xmiTab, xmiTab.getContext()); |
| } |
| } |
| |
| public IModelResource getModelProvider() { |
| return modelProvider; |
| } |
| |
| class ClipboardHandler implements Handler { |
| |
| @Override |
| public void paste() { |
| if (editorTabFolder.getSelectionIndex() == 0) { |
| if (viewer.getControl().getDisplay().getFocusControl() == viewer.getControl()) { |
| handleStructurePaste(); |
| } else if (currentEditor != null) { |
| currentEditor.handlePaste(); |
| } |
| } else { |
| xmiTab.paste(); |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| private void handleStructurePaste() { |
| final Clipboard clip = new Clipboard(viewer.getControl().getDisplay()); |
| Object contents = clip.getContents(MemoryTransfer.getInstance()); |
| clip.dispose(); |
| if (contents == null) { |
| return; |
| } |
| List<EObject> toCopy = new ArrayList<>(); |
| if (contents instanceof EObject) { |
| toCopy.add(EcoreUtil.copy((EObject) contents)); |
| } else if (contents instanceof List<?>) { |
| List<Object> list = (List<Object>) contents; |
| for (Object object : list) { |
| if (object instanceof EObject) { |
| toCopy.add(EcoreUtil.copy((EObject) object)); |
| } |
| } |
| } |
| |
| if (toCopy.isEmpty()) { |
| return; |
| } |
| |
| final Object parent = ((IStructuredSelection) viewer.getSelection()).getFirstElement(); |
| EObject probe = toCopy.get(0); |
| |
| EStructuralFeature feature = null; |
| EObject container = null; |
| if (parent instanceof VirtualEntry) { |
| final VirtualEntry<EObject, ?> v = (VirtualEntry<EObject, ?>) parent; |
| feature = ((IEMFProperty) v.getProperty()).getStructuralFeature(); |
| container = v.getOriginalParent(); |
| } else if (parent instanceof EObject) { |
| container = (EObject) parent; |
| if (container instanceof MElementContainer<?>) { |
| feature = UiPackageImpl.Literals.ELEMENT_CONTAINER__CHILDREN; |
| } else { |
| feature = determineTargetFeature(probe, container); |
| if (feature == null && container.eClass().equals(probe.eClass()) |
| && container.eContainer() != null) { |
| // it seems the user has still the original selection active, |
| // try to find the target feature using the container's container |
| container = container.eContainer(); |
| feature = determineTargetFeature(probe, container); |
| } |
| } |
| } |
| |
| if (container == null) { |
| // no container selected that we can paste into |
| return; |
| } |
| |
| if (feature == null) { |
| // no target feature derivable from current state of the editor |
| return; |
| } |
| |
| List<EClass> targetChildrenClasses = new ArrayList<>(); |
| |
| if (container instanceof MStringModelFragment) { |
| MStringModelFragment stringModelFragment = (MStringModelFragment) container; |
| EClass targetType = StringModelFragment.findContainerType(stringModelFragment); |
| if (targetType != null) { |
| EStructuralFeature targetFeature = targetType |
| .getEStructuralFeature(stringModelFragment.getFeaturename()); |
| if (targetFeature != null) { |
| List<FeatureClass> classes = StringModelFragment.getTargetChildrenClasses(targetType, |
| targetFeature.getName()); |
| for (FeatureClass fclass : classes) { |
| targetChildrenClasses.add(fclass.eClass); |
| } |
| } |
| } |
| } |
| |
| CompoundCommand cc = new CompoundCommand(); |
| Object pastedObject = null; // The single pasted object if single paste (for undo/redo message) |
| for (EObject eObject : toCopy) { |
| if (!isValidTarget(parent, eObject, false)) { |
| // the object to paste does not fit into the target feature |
| continue; |
| } |
| if (!targetChildrenClasses.isEmpty() && !targetChildrenClasses.contains(eObject.eClass())) { |
| // there is a limited list of allowed target types |
| // and a fragment that is pointing to a different feature |
| continue; |
| } |
| |
| if (feature == FragmentPackageImpl.Literals.MODEL_FRAGMENTS__IMPORTS) { |
| final MApplicationElement el = (MApplicationElement) EcoreUtil.create(eObject.eClass()); |
| el.setElementId(((MApplicationElement) eObject).getElementId()); |
| final Command cmd = AddCommand.create(getModelProvider().getEditingDomain(), container, feature, |
| el); |
| if (cmd.canExecute()) { |
| pastedObject = el; |
| cc.append(cmd); |
| } |
| return; |
| } |
| |
| final Command cmd = AddCommand.create(getModelProvider().getEditingDomain(), container, feature, |
| eObject); |
| pastedObject = eObject; |
| if (cmd.canExecute()) { |
| cc.append(cmd); |
| if (isLiveModel()) { |
| if (container instanceof MElementContainer<?> && probe instanceof MUIElement) { |
| // the last selection wins |
| ((MElementContainer<MUIElement>) container).setSelectedElement((MUIElement) eObject); |
| } |
| } |
| } |
| } |
| if (!cc.isEmpty()) { |
| if (cc.getCommandList().size() == 1) { |
| cc.setLabel(messages.ModelEditor_Paste + " " + getObjectNameForCommand(pastedObject)); //$NON-NLS-1$ |
| } else { |
| cc.setLabel(messages.ModelEditor_PasteObjects); |
| } |
| |
| getModelProvider().getEditingDomain().getCommandStack().execute(cc); |
| } |
| } |
| |
| private EStructuralFeature determineTargetFeature(EObject probe, EObject container) { |
| final EClass eClass = container.eClass(); |
| for (final EStructuralFeature f : eClass.getEAllReferences()) { |
| if (ModelUtils.getTypeArgument(eClass, f.getEGenericType()).isInstance(probe)) { |
| return f; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public void copy() { |
| if (editorTabFolder.getSelectionIndex() == 0) { |
| if (viewer.getControl().getDisplay().getFocusControl() == viewer.getControl()) { |
| handleStructureCopy(); |
| } else if (currentEditor != null) { |
| currentEditor.handleCopy(); |
| } |
| } else { |
| xmiTab.copy(); |
| } |
| } |
| |
| private void handleStructureCopy() { |
| IStructuredSelection structuredSelection = (IStructuredSelection) viewer.getSelection(); |
| List<EObject> toCopy = new ArrayList<>(); |
| for (Object obj : structuredSelection.toList()) { |
| if (obj != null && obj instanceof EObject) { |
| EObject copy = EcoreUtil.copy((EObject) obj); |
| toCopy.add(copy); |
| } |
| } |
| if (toCopy.isEmpty()) { |
| return; |
| } |
| final Clipboard clip = new Clipboard(viewer.getControl().getDisplay()); |
| clip.setContents(new Object[] { toCopy }, new Transfer[] { MemoryTransfer.getInstance() }); |
| clip.dispose(); |
| } |
| |
| @Override |
| public void cut() { |
| if (editorTabFolder.getSelectionIndex() == 0) { |
| if (viewer.getControl().getDisplay().getFocusControl() == viewer.getControl()) { |
| handleStructureCut(); |
| } else if (currentEditor != null) { |
| currentEditor.handleCut(); |
| } |
| } else { |
| xmiTab.cut(); |
| } |
| } |
| |
| private void handleStructureCut() { |
| |
| // Manage multiple cut objects (bug #532070) |
| Collection<EObject> objectsToCut = new ArrayList<>(); |
| final Clipboard clip = new Clipboard(viewer.getControl().getDisplay()); |
| for (Object o : ((IStructuredSelection) viewer.getSelection()).toList()) { |
| if (o != null && o instanceof EObject) { |
| objectsToCut.add((EObject) o); |
| } |
| } |
| |
| Command cmd = null; |
| if (objectsToCut.isEmpty()) { |
| return; |
| } else if (objectsToCut.size() == 1) { |
| // Only 1 object to cut... create a removeCommand |
| EObject o = objectsToCut.iterator().next(); |
| cmd = RemoveCommand.create(getModelProvider().getEditingDomain(), o.eContainer(), |
| o.eContainingFeature(), o); |
| ((AbstractCommand) cmd).setLabel(messages.ModelEditor_Cut + " " + getObjectNameForCommand(o)); //$NON-NLS-1$ |
| } else { |
| // There are more than one object to remove -> Compound command. |
| CompoundCommand cc = new CompoundCommand(); |
| cc.setLabel(messages.ModelEditor_CutObjects); |
| for (EObject o : objectsToCut) { |
| cc.append(RemoveCommand.create(getModelProvider().getEditingDomain(), o.eContainer(), |
| o.eContainingFeature(), o)); |
| } |
| cmd = cc; |
| } |
| |
| if (cmd.canExecute()) { |
| // Now can set the clipboard... |
| clip.setContents(new Object[] { objectsToCut }, new Transfer[] { MemoryTransfer.getInstance() }); |
| getModelProvider().getEditingDomain().getCommandStack().execute(cmd); |
| } |
| |
| clip.dispose(); |
| } |
| } |
| |
| public class ObservableFactoryImpl implements IObservableFactory<Object, IObservableList<Object>> { |
| |
| @SuppressWarnings("unchecked") |
| @Override |
| public IObservableList<Object> createObservable(Object target) { |
| if (target instanceof IObservableList) { |
| return new WritableList<>((List<Object>) target, Object.class); |
| } else if (target instanceof VirtualEntry) { |
| return ((VirtualEntry<?, Object>) target).getList(); |
| } else { |
| final AbstractComponentEditor<EObject> editor = (AbstractComponentEditor<EObject>) getEditor( |
| ((EObject) target).eClass()); |
| if (editor != null) { |
| return (IObservableList<Object>) editor.getChildList(target); |
| } |
| } |
| |
| return null; |
| } |
| } |
| |
| static class DragListener extends DragSourceAdapter { |
| private final TreeViewer viewer; |
| |
| public DragListener(TreeViewer viewer) { |
| this.viewer = viewer; |
| } |
| |
| @Override |
| public void dragStart(DragSourceEvent event) { |
| final IStructuredSelection selection = (IStructuredSelection) viewer.getSelection(); |
| event.doit = !selection.isEmpty() && (selection.getFirstElement() instanceof MApplicationElement |
| || selection.getFirstElement() instanceof MModelFragment); |
| } |
| |
| @Override |
| public void dragSetData(DragSourceEvent event) { |
| final IStructuredSelection selection = (IStructuredSelection) viewer.getSelection(); |
| event.data = selection.toArray(); |
| } |
| } |
| |
| class DropListener extends ViewerDropAdapter { |
| private final EditingDomain domain; |
| |
| protected DropListener(Viewer viewer, EditingDomain domain) { |
| super(viewer); |
| this.domain = domain; |
| } |
| |
| @Override |
| public boolean performDrop(Object data) { |
| if (!(data instanceof Object[])) { |
| return false; |
| } |
| final Object[] dropDataArray = (Object[]) data; |
| for (final Object object : dropDataArray) { |
| final boolean result = performSingleDrop(object); |
| if (!result) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| @SuppressWarnings("unchecked") |
| public boolean performSingleDrop(Object data) { |
| if (getCurrentLocation() == LOCATION_ON) { |
| EStructuralFeature feature = null; |
| EObject parent = null; |
| if (getCurrentTarget() instanceof MElementContainer<?>) { |
| feature = UiPackageImpl.Literals.ELEMENT_CONTAINER__CHILDREN; |
| parent = (EObject) getCurrentTarget(); |
| } else if (getCurrentTarget() instanceof VirtualEntry) { |
| |
| final VirtualEntry<EObject, ?> entry = (VirtualEntry<EObject, ?>) getCurrentTarget(); |
| final IListProperty<?, ?> prop = entry.getProperty(); |
| if (prop instanceof IEMFProperty) { |
| feature = ((IEMFProperty) prop).getStructuralFeature(); |
| parent = entry.getOriginalParent(); |
| |
| } |
| } else if (getCurrentTarget() instanceof EObject) { |
| parent = (EObject) getCurrentTarget(); |
| for (final EStructuralFeature f : parent.eClass().getEAllStructuralFeatures()) { |
| final EClassifier cl = ModelUtils.getTypeArgument(parent.eClass(), f.getEGenericType()); |
| if (cl.isInstance(data)) { |
| feature = f; |
| break; |
| } |
| } |
| } |
| |
| if (feature != null && parent != null) { |
| final Command cmd = createRemoveAddCommand(data, feature, parent, CommandParameter.NO_INDEX); |
| if (cmd.canExecute()) { |
| domain.getCommandStack().execute(cmd); |
| if (isLiveModel()) { |
| if (parent instanceof MElementContainer<?> && data instanceof MUIElement) { |
| ((MElementContainer<MUIElement>) parent).setSelectedElement((MUIElement) data); |
| } |
| } |
| return true; |
| } |
| } |
| } else if (getCurrentLocation() == LOCATION_AFTER || getCurrentLocation() == LOCATION_BEFORE) { |
| EStructuralFeature feature = null; |
| EObject parent = null; |
| |
| final TreeItem item = (TreeItem) getCurrentEvent().item; |
| if (item != null) { |
| final TreeItem parentItem = item.getParentItem(); |
| if (parentItem != null) { |
| if (parentItem.getData() instanceof VirtualEntry) { |
| final VirtualEntry<EObject, ?> vE = (VirtualEntry<EObject, ?>) parentItem.getData(); |
| parent = vE.getOriginalParent(); |
| feature = ((IEMFProperty) vE.getProperty()).getStructuralFeature(); |
| } else if (parentItem.getData() instanceof MElementContainer<?>) { |
| parent = (EObject) parentItem.getData(); |
| feature = UiPackageImpl.Literals.ELEMENT_CONTAINER__CHILDREN; |
| } else if (parentItem.getData() instanceof EObject) { |
| parent = (EObject) parentItem.getData(); |
| for (final EStructuralFeature f : parent.eClass().getEAllStructuralFeatures()) { |
| final EClassifier cl = ModelUtils.getTypeArgument(parent.eClass(), f.getEGenericType()); |
| if (cl.isInstance(data)) { |
| feature = f; |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| if (feature == FragmentPackageImpl.Literals.MODEL_FRAGMENTS__IMPORTS && parent != null) { |
| final MApplicationElement el = (MApplicationElement) EcoreUtil.create(((EObject) data).eClass()); |
| el.setElementId(((MApplicationElement) data).getElementId()); |
| final Command cmd = createRemoveAddCommand(data, feature, parent, CommandParameter.NO_INDEX); |
| if (cmd.canExecute()) { |
| domain.getCommandStack().execute(cmd); |
| } |
| return true; |
| } |
| |
| if (feature != null && parent != null && parent.eGet(feature) instanceof List<?>) { |
| final List<Object> list = (List<Object>) parent.eGet(feature); |
| int index = list.indexOf(getCurrentTarget()); |
| |
| if (index >= list.size()) { |
| index = CommandParameter.NO_INDEX; |
| } |
| |
| if (parent == ((EObject) data).eContainer()) { |
| if (parent instanceof MElementContainer<?> && data instanceof MUIElement) { |
| Util.moveElementByIndex(domain, (MUIElement) data, isLiveModel(), index); |
| } else { |
| final Command cmd = MoveCommand.create(domain, parent, feature, data, index); |
| if (cmd.canExecute()) { |
| domain.getCommandStack().execute(cmd); |
| return true; |
| } |
| } |
| } else { |
| // Moving between different sources is always a copy |
| if (parent.eResource() != ((EObject) data).eResource()) { |
| data = EcoreUtil.copy((EObject) data); |
| } |
| |
| final Command cmd = createRemoveAddCommand(data, feature, parent, index); |
| if (cmd.canExecute()) { |
| domain.getCommandStack().execute(cmd); |
| if (isLiveModel()) { |
| if (parent instanceof MElementContainer<?> && data instanceof MUIElement) { |
| ((MElementContainer<MUIElement>) parent).setSelectedElement((MUIElement) data); |
| } |
| } |
| |
| return true; |
| } |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Create an internal Compound command containing a Remove and a Add so as to |
| * allow the Undo |
| * |
| * @param data the object to be dragged and dropped |
| * @param destFeature the target feature in the model where data must be dropped |
| * @param parent the destination parent |
| * @param index the index in the parent list |
| * @see bug 429684 |
| * @return the compound command |
| */ |
| private Command createRemoveAddCommand(Object data, EStructuralFeature destFeature, EObject parent, int index) { |
| |
| // Remark : this code could be replaced by a MoveCommand, but unfortunately I |
| // could not make it working (its canExecute() method always returns false |
| // because it is not prepared (see canExecute())...)... |
| |
| List<Command> listOfCommands = new ArrayList<>(); |
| EStructuralFeature sourceFeature = null; |
| if (data instanceof EObject) { |
| sourceFeature = ((EObject) data).eContainmentFeature(); |
| } |
| Command removeCommand = RemoveCommand.create(domain, ((EObject) data).eContainer(), sourceFeature, data); |
| if (removeCommand.canExecute()) { |
| listOfCommands.add(removeCommand); |
| } |
| |
| Command addCommand = AddCommand.create(domain, parent, destFeature, data, index); |
| listOfCommands.add(addCommand); |
| CompoundCommand compoundCommand = new CompoundCommand(listOfCommands); |
| compoundCommand.setLabel(messages.ModelEditor_Move + " " + getObjectNameForCommand(data)); //$NON-NLS-1$ |
| return compoundCommand; |
| |
| } |
| |
| @Override |
| public boolean validateDrop(Object target, int operation, TransferData transferType) { |
| boolean rv = true; |
| if (getSelectedObject() instanceof MApplicationElement || getSelectedObject() instanceof MModelFragment) { |
| if (getCurrentLocation() == LOCATION_ON) { |
| rv = isValidTarget(target, getSelectedObject(), false); |
| } else if (getCurrentLocation() == LOCATION_AFTER || getCurrentLocation() == LOCATION_BEFORE) { |
| TreeItem item = (TreeItem) getCurrentEvent().item; |
| if (item != null) { |
| item = item.getParentItem(); |
| if (item != null) { |
| rv = isValidTarget(item.getData(), getSelectedObject(), true); |
| } |
| } |
| } |
| } |
| |
| return rv; |
| } |
| |
| } |
| |
| /** |
| * This method checks if the target object is a valid target for the current |
| * instance. It used both for paste and for drag and drop behavior |
| * |
| * @param target the target object where instance should be pasted of dropped |
| * @param instance the instance of object to be pasted or dropped |
| * @param isIndex if true, means that target is an object in a container |
| * @return |
| */ |
| private boolean isValidTarget(Object target, Object instance, boolean isIndex) { |
| if (target instanceof MElementContainer<?>) { |
| @SuppressWarnings("unchecked") |
| final MElementContainer<MUIElement> container = (MElementContainer<MUIElement>) target; |
| |
| if (isIndex || !container.getChildren().contains(instance)) { |
| final EClassifier classifier = ModelUtils.getTypeArgument(((EObject) container).eClass(), |
| UiPackageImpl.Literals.ELEMENT_CONTAINER__CHILDREN.getEGenericType()); |
| return classifier.isInstance(instance); |
| } |
| } else if (target instanceof VirtualEntry) { |
| @SuppressWarnings("unchecked") |
| final VirtualEntry<EObject, ?> vTarget = (VirtualEntry<EObject, ?>) target; |
| if (isIndex || !vTarget.getList().contains(instance)) { |
| if (vTarget.getProperty() instanceof IEMFProperty) { |
| final EStructuralFeature feature = ((IEMFProperty) vTarget.getProperty()).getStructuralFeature(); |
| final EObject parent = vTarget.getOriginalParent(); |
| final EClassifier classifier = ModelUtils.getTypeArgument(parent.eClass(), |
| feature.getEGenericType()); |
| return classifier.isInstance(instance); |
| } |
| |
| } |
| } else if (target instanceof EObject) { |
| final EObject eObj = (EObject) target; |
| for (final EStructuralFeature f : eObj.eClass().getEAllStructuralFeatures()) { |
| final EClassifier cl = ModelUtils.getTypeArgument(eObj.eClass(), f.getEGenericType()); |
| if (cl.isInstance(instance)) { |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * compute a valid name for the undo/redo/paste of move commands |
| * |
| * @param data the object concerned by the command |
| * @return a representative string for the object or 'Object' if nothing found |
| */ |
| private String getObjectNameForCommand(Object data) { |
| String clname = (data instanceof ApplicationElementImpl) ? ((ApplicationElementImpl) data).eClass().getName() |
| : "Object"; //$NON-NLS-1$ |
| String dname = (data instanceof MUILabel) ? ((MUILabel) data).getLabel() : ""; //$NON-NLS-1$ |
| return clname + " " + dname; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public void gotoEObject(int targetHint, EObject object) { |
| if (object == null) { |
| // do nothing |
| } else { |
| switch (targetHint) { |
| case TAB_FORM: |
| // make sure tree node has been instantiated |
| final ObservableListTreeContentProvider<?> provider = (ObservableListTreeContentProvider<?>) viewer |
| .getContentProvider(); |
| getFirstMatchingItem(object, provider, provider.getChildren(viewer.getInput())); |
| |
| viewer.reveal(object); |
| viewer.setSelection(new StructuredSelection(object)); |
| editorTabFolder.setSelection(getTabIndex(tabItemTree)); |
| break; |
| case TAB_XMI: |
| editorTabFolder.setSelection(getTabIndex(tabItemXmi)); |
| // model was not updating in XMI document (selection listener |
| // was not firing from programmatic setSelection() |
| emfDocumentProvider.updateFromEMF(); |
| |
| try { |
| xmiTab.gotoEObject(object); |
| } catch (final Exception e) { |
| e.printStackTrace(); |
| } |
| break; |
| case TAB_LIST: |
| if (tabItemList != null && listTab != null) { |
| editorTabFolder.setSelection(getTabIndex(tabItemList)); |
| listTab.getViewer().setSelection(new StructuredSelection(object), true); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| |
| // This will ensure the provider has created the tree node (so we can reveal |
| // it). |
| private static Object getFirstMatchingItem(EObject target, ObservableListTreeContentProvider<?> provider, |
| Object[] items) { |
| for (int i = 0; i < items.length; i++) { |
| if (items[i] == target) { |
| return items[i]; |
| } |
| final Object found = getFirstMatchingItem(target, provider, provider.getChildren(items[i])); |
| if (found != null) { |
| return found; |
| } |
| } |
| return null; |
| } |
| |
| public void refreshViewer() { |
| viewer.refresh(true); |
| } |
| |
| @Inject |
| @Optional |
| public void refreshOnSave(@UIEventTopic(UIEvents.Dirtyable.TOPIC_DIRTY) org.osgi.service.event.Event event, |
| @Named(IServiceConstants.ACTIVE_PART) MPart part) { |
| // If the application model is saved (-> becomes undirty) we must |
| // refresh tree (bug 472706) |
| // Must react only if editor is the current one... (bug 509598) |
| if (part != currentPart) { |
| return; |
| } |
| |
| final Object type = event.getProperty(EventTags.TYPE); |
| final Object newValue = event.getProperty(EventTags.NEW_VALUE); |
| |
| if (UIEvents.EventTypes.SET.equals(type) && Boolean.FALSE.equals(newValue) && viewer != null) { |
| viewer.refresh(true); |
| } |
| |
| } |
| |
| } |