/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.mapping.ui.editor; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.edit.provider.INotifyChangedListener; import org.eclipse.jface.viewers.DecoratingLabelProvider; import org.eclipse.jface.viewers.ILabelDecorator; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.ITreeViewerListener; import org.eclipse.jface.viewers.LabelProviderChangedEvent; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TreeExpansionEvent; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.ScrollBar; import org.teiid.designer.core.notification.util.NotificationUtilities; import org.teiid.designer.diagram.ui.DiagramUiPlugin; import org.teiid.designer.mapping.factory.ModelMapperFactory; import org.teiid.designer.mapping.ui.PluginConstants; import org.teiid.designer.mapping.ui.diagram.MappingDiagramUtil; import org.teiid.designer.metamodels.transformation.MappingClass; import org.teiid.designer.metamodels.transformation.MappingClassColumn; import org.teiid.designer.ui.common.eventsupport.SelectionUtilities; import org.teiid.designer.ui.event.IRevealHideListener; import org.teiid.designer.ui.viewsupport.ModelUtilities; /** * DocumentTreeController * * @since 8.0 */ public class DocumentTreeController implements ITreeViewerListener, ISelectionProvider, ISelectionChangedListener { MappingDiagramController diagramController; ScrollBar docTreeVertScrollBar; private static final int ROW_HEIGHT = 16; int verticalScrollValue = 0; private DocumentTreeViewer viewer; XmlDocumentModelObjectLabelProvider provider; private ExtendedDecoratingLabelProvider decLabelProvider; public DocumentTreeController( MappingDiagramController mdc ) { this.diagramController = mdc; } public Control getControl() { return viewer.getControl(); } /** * @see org.eclipse.ui.IWorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) */ public void createControl( Composite theParent ) { viewer = new DocumentTreeViewer(theParent); viewer.setUseHashlookup(true); /* * jh Lyra enh: Changing this to use a custom label provider (based on the EMF label provider) */ provider = new XmlDocumentModelObjectLabelProvider(this); ILabelDecorator decorator = DiagramUiPlugin.getDefault().getWorkbench().getDecoratorManager().getLabelDecorator(); decLabelProvider = new ExtendedDecoratingLabelProvider(provider, decorator); viewer.setLabelProvider(decLabelProvider); viewer.addTreeListener(this); docTreeVertScrollBar = viewer.getTree().getVerticalBar(); docTreeVertScrollBar.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected( SelectionEvent theEvent ) { // don't process drag events since there are so many of them. another event // is received when the drag is done // bmlFIXME: Looks like the scroll widget doesn't broadcast anything on "Drag Scroll" other than SWT.DRAG. // Don't know what we are going to do here, but this is updating all the time now (i.e. several times per drag) if (verticalScrollValue != docTreeVertScrollBar.getSelection()) { resetExtentsFromDocument(); } } }); } public void resetExtentsFromDocument() { // getSelection() is returning a pixel value for the upper scroll bar location // (int)(0.5f + ((maximum - thumb - minimum) * value + minimum)); int newY = 0 - docTreeVertScrollBar.getSelection(); verticalScrollValue = docTreeVertScrollBar.getSelection()/ROW_HEIGHT; diagramController.resetExtentLocationsFromDocument(newY); } public MappingAdapterFilter getMappingAdapterFilter() { return viewer.getMappingAdapterFilter(); } public MappingAdapterFilter getMappingAdapterFilter( boolean bForceRecreate ) { return viewer.getMappingAdapterFilter(bForceRecreate); } public XmlDocumentModelObjectLabelProvider getXmlDocumentModelObjectLabelProvider() { return provider; } /** * @see org.eclipse.jface.viewers.ITreeViewerListener#treeCollapsed(org.eclipse.jface.viewers.TreeExpansionEvent) */ @Override public void treeCollapsed( final TreeExpansionEvent event ) { // System.out.println("[DocumentTreeController.treeCollapsed] event: " + event.getElement().toString() ); Display.getDefault().asyncExec(new Runnable() { @Override public void run() { IRevealHideListener rhl = diagramController.getRevealHideListener(); if (rhl != null && rhl.isRevealHideBehaviorEnabled()) { List lstChildren = getChildrenOfExpandedNode((EObject)event.getElement(), diagramController.getMappingFilter()); rhl.notifyElementsHidden(DocumentTreeController.this, lstChildren); } /* * Changed reconcileMC's to TRUE because if collapsed, we need to make sure any extra mapping classes are removed * from the diagram. */ diagramController.refresh(true); resetExtentsFromDocument(); } }); } /** * @see org.eclipse.jface.viewers.ITreeViewerListener#treeExpanded(org.eclipse.jface.viewers.TreeExpansionEvent) */ @Override public void treeExpanded( final TreeExpansionEvent event ) { // System.out.println("[DocumentTreeController.treeExpanded] event: " + event.getElement().toString() ); Display.getDefault().asyncExec(new Runnable() { @Override public void run() { IRevealHideListener rhl = diagramController.getRevealHideListener(); if (rhl != null && rhl.isRevealHideBehaviorEnabled()) { List lstChildren = getChildrenOfExpandedNode((EObject)event.getElement(), diagramController.getMappingFilter()); rhl.notifyElementsRevealed(DocumentTreeController.this, lstChildren); } /* * Changed reconcileMC's to TRUE because if collapsed, we need to make sure any new mapping classes are added * to the diagram. */ diagramController.refresh(true); resetExtentsFromDocument(); } }); } List getChildrenOfExpandedNode( EObject eo, MappingAdapterFilter xmlFilter ) { List lstResult = new ArrayList(); DocumentContentProvider dcpContentProvider = (DocumentContentProvider)xmlFilter.getTreeViewer().getContentProvider(); Object[] children = dcpContentProvider.getChildren(eo); for (int i = 0; i < children.length; i++) { lstResult.add(children[i]); } return lstResult; } /** * @see org.eclipse.jface.viewers.ISelectionProvider#addSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener) */ @Override public void addSelectionChangedListener( ISelectionChangedListener listener ) { this.viewer.addSelectionChangedListener(listener); } /** * @see org.eclipse.jface.viewers.ISelectionProvider#getSelection() */ @Override public ISelection getSelection() { return this.viewer.getSelection(); } /** * @see org.eclipse.jface.viewers.ISelectionProvider#removeSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener) */ @Override public void removeSelectionChangedListener( ISelectionChangedListener listener ) { this.viewer.removeSelectionChangedListener(listener); } /** * @see org.eclipse.jface.viewers.ISelectionProvider#setSelection(org.eclipse.jface.viewers.ISelection) */ @Override public void setSelection( ISelection selection ) { this.viewer.setSelection(selection, true); } public DocumentTreeViewer getViewer() { return this.viewer; } /** * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent) */ @Override public void selectionChanged( SelectionChangedEvent event ) { // System.out.println( "[DocumentTreeController.selectionChanged] TOP" ); ISelection selection = event.getSelection(); // walk through selected objects, and add all to list as long as they are either a MappingClass // or a MappingClassColumn List selections = SelectionUtilities.getSelectedEObjects(selection); List mappingSelections = new ArrayList(selections.size()); List nodeSelections = new ArrayList(selections.size()); Iterator iter = selections.iterator(); boolean allSelectionsValid = true; boolean selectedMappings = false; boolean selectedTreeNodes = false; if (selections.size() < 1) allSelectionsValid = false; while (iter.hasNext() && allSelectionsValid) { Object obj = iter.next(); if (obj instanceof MappingClass || obj instanceof MappingClassColumn) { mappingSelections.add(obj); selectedMappings = true; } else if (ModelMapperFactory.isXmlTreeNode((EObject)obj)) { nodeSelections.add(obj); selectedTreeNodes = true; } else { // Don't know how to hilite anything but mapping class objects // in document tree, so we don't send it anything. allSelectionsValid = false; } } if (allSelectionsValid) { if (selectedMappings) { viewer.setSelectedMapping(mappingSelections); resetExtentsFromDocument(); } else if (selectedTreeNodes) { // jh Lyra enh viewer.setSelectedNodes(nodeSelections); resetExtentsFromDocument(); // jh Defect 22096: Should only do the refresh IF in COARSE, and the // 'Populate Diagram From Tree Selection' feature is turned on. if (diagramController != null && diagramController.getMappingType() == PluginConstants.COARSE_MAPPING) { boolean bPopulateDiagramFromTreeSelection = MappingDiagramUtil.getCurrentMappingDiagramBehavior().getPopulateDiagramFromTreeSelectionState(); if (bPopulateDiagramFromTreeSelection) { refreshOnTreeSelection(); } } } } else { if (!event.getSource().equals(this.viewer)) viewer.showNoneSelected(); else { viewer.clearAllHilites(); diagramController.clearDiagramSelection(); } } } /** * @see org.eclipse.jface.viewers.ITreeViewerListener#treeCollapsed(org.eclipse.jface.viewers.TreeExpansionEvent) */ public void refreshOnTreeSelection() { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { /* * jh fix: This isDisposed check protects us from the case of this method being * called because of an tree select event that happens during a CLOSE. * Because this is async it will then happen after the close, and the * tree will no longer be there, causing a 'Widget is disposed' error. * This check prevents that from happening. */ if (!getViewer().getControl().isDisposed()) { diagramController.refresh(false); } } }); } public void reveal( Object eObject ) { } /** * @since 5.0 */ public void dispose() { // Defect 22290 reflects memory (leaks) issues within designer. // remove listeners with disposed. decLabelProvider.removeListeners(); } class ExtendedDecoratingLabelProvider extends DecoratingLabelProvider { INotifyChangedListener notifyChangedListener; public ExtendedDecoratingLabelProvider( ILabelProvider provider, ILabelDecorator decorator ) { super(provider, decorator); addListener(); } public void removeListeners() { // Defect 22290 reflects memory (leaks) issues within designer. // remove listeners with disposed. ModelUtilities.removeNotifyChangedListener(notifyChangedListener); } private void addListener() { notifyChangedListener = new INotifyChangedListener() { /** * @see org.eclipse.emf.edit.provider.INotifyChangedListener#notifyChanged(org.eclipse.emf.common.notify.Notification) * @since 4.3 */ @Override public void notifyChanged( Notification notification ) { final Display display = Display.getDefault(); if (display.isDisposed()) { return; } EObject eo = NotificationUtilities.getEObject(notification); if (eo != null) { display.asyncExec(new Runnable() { @Override public void run() { changeLabel(); } }); } } }; ModelUtilities.addNotifyChangedListener(notifyChangedListener); } void changeLabel() { LabelProviderChangedEvent event = new LabelProviderChangedEvent(provider.getLabelProviderChangedEventSource(), null); fireLabelProviderChanged(event); } } }