/*****************************************************************************
* Copyright (c) 2009 CEA LIST & LIFL
*
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation
*
*****************************************************************************/
package org.eclipse.papyrus.infra.core.sasheditor.internal;
import static org.eclipse.papyrus.infra.core.sasheditor.Activator.log;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.util.Geometry;
import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.IComponentModel;
import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.IEditorModel;
import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.IPageModel;
import org.eclipse.papyrus.infra.core.sasheditor.contentprovider.ISashWindowsContentProvider;
import org.eclipse.papyrus.infra.core.sasheditor.editor.IEditorPage;
import org.eclipse.papyrus.infra.core.sasheditor.editor.IPage;
import org.eclipse.papyrus.infra.core.sasheditor.editor.IPageChangedListener;
import org.eclipse.papyrus.infra.core.sasheditor.editor.IPageLifeCycleEventsListener;
import org.eclipse.papyrus.infra.core.sasheditor.editor.IPageVisitor;
import org.eclipse.papyrus.infra.core.sasheditor.editor.ISashWindowsContainer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.internal.DragCursors;
import org.eclipse.ui.internal.dnd.DragUtil;
import org.eclipse.ui.internal.dnd.IDragOverListener;
import org.eclipse.ui.internal.dnd.IDropTarget;
/**
* Main entry class of the SashWindows system.
* This class allows to have a multitab window with sashes.
* The class require a ContentProvider describing the content to be shown.
*
* @author dumoulin
*/
@SuppressWarnings("restriction")
public class SashWindowsContainer implements ISashWindowsContainer {
/**
* The content provider describing the sashes, folders and tabs.
*/
private ISashWindowsContentProvider contentProvider;
/**
* The manager used to get Main editor properties like Site, ActionBars, ...
*/
private IMultiEditorManager multiEditorManager;
/**
* Tracker tracking the current active page. The tracker also disconnect last active page and connect
* the new one.
*/
private ActivePageTracker activePageTracker;
/**
* Event provider firing Pages life cycle events to registered listeners. Inner parts call the fireXxxEvents
* when appropriate.
*/
private SashContainerEventsProvider lifeCycleEventProvider;
/**
* The part used as root. We use an extra class as root in order to separate the code dedicated to
* ITilePart.
*/
private RootPart rootPart;
/**
* The SWT container associated to this part. This is generally the container of the
* parent.
*/
private Composite container;
/**
* The drop target.
*/
protected DropTarget dropTarget;
/** A flag that indicates that the model is being synchronized. */
private AtomicBoolean isRefreshing = new AtomicBoolean(false);
/**
* The cached value of the menu manager, if any.
*/
private MenuManager folderTabMenuManager;
/**
* Listener on widget diposed event.
*/
private DisposeListener widgetDisposedListener = new DisposeListener() {
/**
* Called when the widget is disposed.
* @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
*
* @param e
*/
public void widgetDisposed(DisposeEvent e) {
// We dispose the container.
dispose();
}
};
/**
* Constructor.
* Build a Container without IEditor management. Trying to add a EditorPart will result in an Exception.
* The ContentProvider should not contain IEditorModel.
*/
public SashWindowsContainer() {
this(null);
}
/**
* Constructor.
* Build a container with EditorPart management. The container will allow to add EditorPart
* (and thus IEditorModel to the ContentProvider).
*
* @param multiEditorManager
* The manager allowing to use {@link IEditorModel} in the model.
* If null, the sash will not render IEditorModel.
*
*/
public SashWindowsContainer(IMultiEditorManager multiEditorManager) {
this.multiEditorManager = multiEditorManager;
activePageTracker = new ActivePageTracker();
if(multiEditorManager != null) {
// Add listener on activePageChange.
// This listener will take in charge editor services switching.
activePageTracker.addActiveEditorChangedListener(new ActiveEditorServicesSwitcher(multiEditorManager.getEditorSite()));
}
// Life cycle event provider
lifeCycleEventProvider = new SashContainerEventsProvider();
}
/**
* @return the contentProvider
*/
protected ISashWindowsContentProvider getContentProvider() {
// Content provider should have been set.
assert (contentProvider != null);
// Double check for developement
if(contentProvider == null)
throw new IllegalStateException("ContentProvider should be set before calling any method requiring it.");
return contentProvider;
}
/**
* Set the content provider describing the sashes, folders and tabs.
*
* @param contentProvider
* the contentProvider to set
*/
public void setContentProvider(ISashWindowsContentProvider contentProvider) {
this.contentProvider = contentProvider;
}
/**
* Creates control associated to this Container.
* This method should be called when the parent is build.
*
* @param parent
* The parent in which the editor should be created; must not be <code>null</code>.
*/
public void createPartControl(Composite parent) {
this.container = parent;
rootPart = createRootPart();
// Create the tree of tile part.
rootPart.createPartControl(container);
// Create children
refreshTabs();
// Set selection
selectPage(lookupFirstValidPage());
// postCreatePartControl();
// TODO reactivate next
initDrag(container);
// activate();
// Listen for disposale
container.addDisposeListener(widgetDisposedListener );
}
/**
* Create the root part for the model.
*/
private RootPart createRootPart() {
RootPart part = new RootPart(this);
return part;
}
/**
* Dispose the Container. All referenced resources will be disposed.
* The container should not be used anymore once disposed.
* The result of calling a method after a dispose() is unpredictable.
* <br>
* This method can be called several times.
* <br>
* <br>
* How the method works:
* <ul>
* <li>The {@link SashWindowsContainer} has two trees, the SWT tree and a Part tree ({@link #rootPart}).</li>
* <li>The SWT tree is disposed first. </li>
* <ul>
* <li>This prevent events fired from user interaction or from Widget modifiaction</li>
* <li>The SWT disposal stop before nested editors SWT (thanks to the DISPOSE event in {@link EditorPart}).
* At this point, the nested editor dispose() method is called.
* </li>
* <li>This allow to let the nested editor receive one single dispose call.</li>
* <li></li>
* </ul>
* <li>The Part tree is disposed second (by calling rootPart.disposeThisAndChildren() )</li>
* <ul>
* <li>properties are cleaned in order to help the GC</li>
* <li>swt controls are not disposed again</li>
* </ul>
* <li></li>
* <li></li>
* <li></li>
* <li></li>
* <li></li>
* </ul>
*
*/
public void dispose() {
// Check if already disposed
if( isDisposed() ) {
return;
}
// End disposing children's SWT controls.
// It is possible to recall the dispose() method on a Widget, even if we are called by the dispose event.
// Recalling the dispose method will continue disposing SWT children's.
container.dispose();
// dispose part children
rootPart.disposeThisAndChildren();
// clean up properties to help GC
activePageTracker = null;
container = null;
contentProvider = null;
dragOverListener = null;
folderTabMenuManager = null;
lifeCycleEventProvider = null;
multiEditorManager = null;
rootPart = null;
}
/**
* Return true if the container is disposed, false otherwise.
*
* @return
*/
public boolean isDisposed() {
// Use the activePageTracker as a flag.
return activePageTracker == null;
}
/**
* Notifies this page container that the specified page has been activated. This method
* is called after the current tabs has been changed, either by refreshing the tabs, or by a user
* UI action.
* This method just set correctly the active page value in the Container, and fire pageChanged events if needed.
* It does not change the selected page in the Part.
*
* Propagate the event to activePageTracker.
*
* @param childPart
*/
protected void pageChanged(PagePart childPart) {
activePageTracker.setActiveEditor(childPart);
lifeCycleEventProvider.firePageActivatedEvent(childPart);
}
/**
* Notifies this page container that a pageChanged event has been fired by one swt Control.
* This method is usually called after the user selects a different tab.
*
* The method record the new active folder in the ContentProvider, and calls {@link #pageChanged(PagePart)}.
*
* @param childPart
*/
protected void pageChangedEvent(PagePart childPart) {
// Check if it is really a change before changing the model (which can throw change event)
// The folder model change is done before the tracker fires the listeners, like this
// listeners can check the model.
if(getActivePage() == childPart)
return;
contentProvider.setCurrentFolder(childPart.getParent().getRawModel());
pageChanged(childPart);
}
/**
* Set the active page. The current active page will be the specified page.
* Do not record the new active folder in the ContentProvider
*
* The method record the new CurrentFolder, and calls {@link #pageChanged(PagePart)}.
*
* @param childPart
*/
protected void setActivePage(PagePart childPart) {
pageChanged(childPart);
}
/**
* A change has happen in one of the inner parts. Relay the event.
* This method is called by inner parts whenever the event happen in one of the part.
* It collects and relay the firePropertyChange(int propertyId) calls from the inner IEditor.
*
* @param propertyId
*/
protected void firePropertyChange(int propertyId) {
// For now, we do nothing with this event.
}
/**
* Create the part for the specified newModel.
*
* @param parent
* The parent of the created part.
* @param partModel
* The model for which a part should be created.
* @return
*/
protected PagePart createPagePart(TabFolderPart parent, IPageModel partModel, Object rawModel) {
if(partModel instanceof IEditorModel) {
// Check if we can use IEditorModel
if(multiEditorManager == null)
throw new IllegalArgumentException("Container can't accept IEditorModel as no IMultiEditorManager is set. Please set a IMultiEditorManager.");
return new EditorPart(parent, (IEditorModel)partModel, rawModel, multiEditorManager);
} else if(partModel instanceof IComponentModel) {
return new ComponentPart(parent, (IComponentModel)partModel, rawModel);
} else {
// Return a default part
}
// TODO return a default part showing an error instead.
throw new IllegalArgumentException("No Part found for the model '" + rawModel + "'");
}
/**
* Get the active page.
*
* @return
*/
private PagePart getActivePage() {
return activePageTracker.getActiveEditor();
}
/**
* @see org.eclipse.papyrus.infra.core.sasheditor.editor.ISashWindowsContainer#getActiveEditor()
* @return
*
*/
public IEditorPart getActiveEditor() {
PagePart pagePart = getActivePage();
if(pagePart instanceof EditorPart)
return ((EditorPart)pagePart).getIEditorPart();
else
return null;
}
/**
* Get the active page public API.
*
* @return
*/
public IPage getActiveSashWindowsPage() {
return getActivePage();
}
/**
* Get the list of visible IPages. The visible IPages are the one that have there diagram area
* visible.
*
* @return
*/
public List<IPage> getVisiblePages() {
CollectVisiblePageVisitor visitor = new CollectVisiblePageVisitor();
rootPart.visit(visitor);
return visitor.getVisiblePages();
}
/**
* Get the list of visible IPages. The visible IPages are the one that have there diagram area
* visible.
*
* @return
*/
// public List<IEditorPage> getVisibleIEditorPages() {
// CollectVisiblePageVisitor visitor = new CollectVisiblePageVisitor( IEditorPage.class);
//
// rootPart.visit(visitor);
//
// return visitor.getVisiblePages();
// }
/**
* Get the list of visible IPages. The visible IPages are the one that have there diagram area
* visible.
*
* @return
*/
public List<IEditorPart> getVisibleIEditorParts() {
CollectVisibleIEditorPart visitor = new CollectVisibleIEditorPart();
rootPart.visit(visitor);
return visitor.getVisiblePages();
}
/**
* @see org.eclipse.papyrus.infra.core.sasheditor.editor.ISashWindowsContainer#setFocus()
*
*/
public void setFocus() {
setFocus(getActivePage());
}
/**
* Sets focus to the control for the given page. If the page has an editor,
* this calls its <code>setFocus()</code> method. Otherwise, this calls <code>setFocus</code> on the control for the page.
*
* @param pageIndex
* the index of the page
*/
private void setFocus(PagePart part) {
if(part != null)
part.setFocus();
}
/**
* Refresh the tabs.
* Is we are already currently refreshing, simply return.
*
* @see org.eclipse.papyrus.infra.core.sasheditor.editor.ISashWindowsContainer#refreshTabs()
*
*/
public void refreshTabs() {
// Check if we arent already refreshing
if(isRefreshing.compareAndSet(false, true)) {
try {
refreshTabsInternal();
} finally {
isRefreshing.set(false);
}
} else {
log.warn("refresh inside refresh !");
}
}
/**
* Refresh the tab of the page, (i.e the name and icon in the page's tab).
*
* @param page The page for which the name and icon should be refreshed.
*/
public void refreshPageTab(IPage page) {
if( page instanceof PagePart)
{
((PagePart)page).refreshTab();
}
else
{
// TODO : lookup for the corresponding PagePart, and call refresh.
}
}
/**
* Real implementation of refreshTab.
*
* @see org.eclipse.papyrus.infra.core.sasheditor.editor.ISashWindowsContainer#refreshTabs()
*
*/
private void refreshTabsInternal() {
// System.out.println("start synchronize2() ------------------------");
// showTilesStatus();
// Get the currently selected folder
PagePart oldActivePage = getActivePage();
// Do refresh
container.setRedraw(false);
// Create map of parts
// PartMap<T> partMap = new PartMap<T>();
PartLists garbageMaps = new PartLists();
rootPart.fillPartMap(garbageMaps);
// Synchronize parts
rootPart.synchronize2(garbageMaps);
// Remove orphaned parts (no more used)
garbageMaps.garbage();
// set active page if needed
setActivePage(checkAndGetActivePage(oldActivePage, garbageMaps));
// Reenable SWT and force layout
container.setRedraw(true);
container.layout(true, true);
// System.out.println("end synchronize2() ------------------------");
// showTilesStatus();
}
/**
* Select the specified page in the Parts. The specified page will becomes the active one.
* Appropriate events are fired.
* This is the programatic counterpart of selecting a page in the UI.
* If the provided page is null, do nothing.
*
* @param page
* The page to select or null.
*/
protected void selectPage(PagePart page) {
if(page == null)
return;
TabFolderPart folder = page.getParent();
folder.setActiveEditor(page);
}
/**
* Select the specified page in the Parts. The specified page will becomes the active one.
* Appropriate events are fired.
* This is the programatic counterpart of selecting a page in the UI.
* If the provided page is null, do nothing.
*
* @param page
* The page to select or null. The IPage should
* be an instance previously returned by the SashContainer.
*/
public void selectPage(IPage page) {
if(page == null)
return;
// check if we are a correct instance.
if(!(page instanceof PagePart))
return;
selectPage((PagePart)page);
}
/**
* Lookup the {@link IPage} used to render the specified rawModel.
*
* @param rawModel
* The model for which the IPage is requested.
* If the model is not rendered, return null;
*
* @return The corresponding IPage or null if not found.
*/
public IPage lookupModelPage(Object rawModel) {
// Use a visitor to lookup the first IPage
LookupModelPageVisitor visitor = new LookupModelPageVisitor(rawModel);
rootPart.visit(visitor);
return visitor.result();
}
/**
* Lookup the {@link IPage} used to render the specified IEditorPart.
*
* @param editor
* The IEditorPart for which the IPage is requested.
* If the IEditorPart is not rendered, return null;
*
* @return The corresponding IPage or null if not found.
*/
public IPage lookupIPageByIEditorPart(IEditorPart editor) {
// Use a visitor to lookup the first IPage
LookupIPageByIEditorPartVisitor visitor = new LookupIPageByIEditorPartVisitor(editor);
rootPart.visit(visitor);
return visitor.result();
}
/**
* Check if the oldActivePage still alive, and set it if needed.
* If the oldActivePage is null, set an active page if one exist.
* If the oldActivePage still alive, let it as the active one. If it is
* disposed, get arbitrarily an active page if one exist.
*
* @param oldActivePage
* @param partLists
* @param garbageMaps
* @return A valid active page or null if none exists.
*/
private PagePart checkAndGetActivePage(PagePart oldActivePage, PartLists partLists) {
// Check if there is a created page
PagePart activePage = partLists.getFirstCreatedPage();
if(activePage != null)
return activePage;
// Check oldActivePage validity (in case it has been deleted)
if(oldActivePage != null && !(oldActivePage.isOrphaned() || oldActivePage.isUnchecked()))
return oldActivePage;
// Get an active page if any
return lookupFirstValidPage();
}
/**
* Lookup for a valid active Page. Return null if none is found.
* TODO Use a visitor to implements this method.
*
* @return
*/
private PagePart lookupFirstValidPage() {
// First get a list of active editors
PartLists garbageMaps = new PartLists();
rootPart.fillPartMap(garbageMaps);
return garbageMaps.getFirstValidPage();
}
/**
* Set a {@link MenuManager} used to manage a contextual menu that is shown on the tabs area of the folders.
*
* @param menuManager
* The {@link MenuManager} used to create the menu on the tab area.
*/
public void setFolderTabMenuManager(MenuManager menuManager) {
this.folderTabMenuManager = menuManager;
// Set the MenuManager in each existing folder.
// Use a visitor.
SetFolderTabMenuVisitor visitor = new SetFolderTabMenuVisitor(menuManager);
rootPart.visit(visitor);
}
/**
* @return the menuManager
*/
protected MenuManager getFolderTabMenuManager() {
return folderTabMenuManager;
}
/**
* Show the status of the different Tiles composing the sash system.
* Used for debug purpose.
*/
public void showTilesStatus() {
ShowPartStatusVisitor visitor = new ShowPartStatusVisitor();
rootPart.visit(visitor);
}
/**
* Visit all the Pages (IEditorPage and IComponentPage), allowing to access to the public interface.
*/
public void visit(IPageVisitor pageVisitor) {
PageVisitorWrapper visitor = new PageVisitorWrapper(pageVisitor);
rootPart.visit(visitor);
}
/**
* Visit the Part associated to the container. This method visibility is protected in order to be able to access it
* from junit tests.
* It is not intended to be used by public API or from outside.
*/
protected void visit(IPartVisitor visitor) {
rootPart.visit(visitor);
}
/* ***************************************************** */
/* Drag and Drop methods */
/* ***************************************************** */
/**
*
*/
private void initDrag(Composite container) {
DragUtil.addDragTarget(container, dragOverListener);
}
IDragOverListener dragOverListener = new IDragOverListener() {
/**
*
* @see org.eclipse.ui.internal.dnd.IDragOverListener#drag(org.eclipse.swt.widgets.Control, java.lang.Object, org.eclipse.swt.graphics.Point,
* org.eclipse.swt.graphics.Rectangle)
*/
public IDropTarget drag(Control currentControl, Object draggedObject, Point position, Rectangle dragRectangle) {
// TODO remove the cast by changing the method. Only folder can be source and target
final TabFolderPart sourcePart = (TabFolderPart)rootPart.findPart(draggedObject); // (ITilePart) draggedObject;
// Compute src tab index
// TODO move that and previous in the sender of drag event. Use a class containing both as draggedObject.
final int srcTabIndex = PTabFolder.getDraggedObjectTabIndex(draggedObject);
// System.out.println("drag to position=" + position);
Rectangle containerDisplayBounds = DragUtil.getDisplayBounds(container);
AbstractPanelPart targetPart = null;
// Check if the cursor is inside the container
if(containerDisplayBounds.contains(position)) {
if(rootPart != null) {
targetPart = (AbstractPanelPart)rootPart.findPart(position);
// System.out.println("targetPart=" + targetPart
// + ", position=" + position
// + "container.toControl(position)=" + container.toControl(position));
}
if(targetPart != null) {
final Control targetControl = targetPart.getControl();
final Rectangle targetBounds = DragUtil.getDisplayBounds(targetControl);
int side = Geometry.getClosestSide(targetBounds, position);
int distance = Geometry.getDistanceFromEdge(targetBounds, position, side);
// Reserve the 5 pixels around the edge of the part for the drop-on-edge cursor
// Check if the target can handle the drop.
if(distance >= 5) {
// Otherwise, ask the part if it has any special meaning for this drop location
// TODO remove cast; change return type of findPart()
IDropTarget target = targetPart.getDropTarget(draggedObject, (TabFolderPart)sourcePart, position);
if(target != null) {
return target;
}
} else {
// We are on the boarder, try to drop on the parent
// Warning : the parent could be the rootPart
// System.out.println("DropTarget near the border");
}
//
if(distance > 30) {
side = SWT.CENTER;
}
//
// // If the part doesn't want to override this drop location then drop on the edge
//
// // A "pointless drop" would be one that will put the dragged object back where it started.
// // Note that it should be perfectly valid to drag an object back to where it came from -- however,
// // the drop should be ignored.
//
@SuppressWarnings("unused")
boolean pointlessDrop = false;
if(sourcePart == targetPart) {
pointlessDrop = true;
}
return createDropTarget(sourcePart, srcTabIndex, side, side, targetPart);
}
} else {
// Cursor is outside the container
// System.out.println("Outside container bounds");
// This will be used to create a new Window.
// We only allow dropping into a stack, not creating one
// if (differentWindows)
// return null;
int side = Geometry.getClosestSide(containerDisplayBounds, position);
boolean pointlessDrop = false;
int cursor = Geometry.getOppositeSide(side);
if(pointlessDrop) {
side = SWT.NONE;
}
return createDropTarget(sourcePart, srcTabIndex, side, cursor, null);
}
return null;
}
};
/**
* Create the drop target
*/
private DropTarget createDropTarget(final TabFolderPart sourcePart, int srcTabIndex, int side, int cursor, AbstractPart targetPart) {
if(dropTarget == null) {
dropTarget = new DropTarget(sourcePart, srcTabIndex, side, cursor, targetPart);
} else {
dropTarget.setTarget(sourcePart, srcTabIndex, side, cursor, targetPart);
}
return dropTarget;
}
/**
* Class implementing methods required by drop targets.
*/
protected class DropTarget implements IDropTarget {
int count = 0;
int cursor = SWT.TOP;
private int side;
private AbstractPanelPart targetPart;
private int srcTabIndex;
private TabFolderPart sourcePart;
/**
* Constructor.
*/
public DropTarget(TabFolderPart sourcePart, int srcTabIndex, int cursor, int side, AbstractPart targetPart) {
this.cursor = cursor;
this.side = side;
this.sourcePart = sourcePart;
this.srcTabIndex = srcTabIndex;
this.targetPart = (AbstractPanelPart)targetPart;
}
public void setTarget(TabFolderPart sourcePart, int srcTabIndex, int cursor, int side, AbstractPart targetPart) {
this.cursor = cursor;
this.side = side;
this.sourcePart = sourcePart;
this.srcTabIndex = srcTabIndex;
this.targetPart = (AbstractPanelPart)targetPart;
}
/**
* A folder is dropped.
*
* @see org.eclipse.ui.internal.dnd.IDropTarget#drop()
*/
public void drop() {
// @TODO remove next cast
if(side == SWT.CENTER) { // Add to target folder
contentProvider.movePage(sourcePart.getPartModel(), srcTabIndex, ((TabFolderPart)targetPart).getPartModel(), -1);
} else { // Create a new folder
contentProvider.createFolder(sourcePart.getPartModel(), srcTabIndex, ((TabFolderPart)targetPart).getPartModel(), side);
}
}
/**
* Return the cursor used during drag.
*
* @see org.eclipse.ui.internal.dnd.IDropTarget#getCursor()
*/
public Cursor getCursor() {
// System.out.println(SashWindowsContainer.this.getClass().getSimpleName() + ".getCursor()-" + count++);
return DragCursors.getCursor(DragCursors.positionToDragCursor(cursor));
}
public Rectangle getSnapRectangle() {
// System.out.println(SashWindowsContainer.this.getClass().getSimpleName() + ".getSnapRectangle(" + "sourcePart=" + sourcePart + ", targetPart=" + targetPart + ", side=" + side);
Rectangle targetDisplayBounds;
if(targetPart != null) {
targetDisplayBounds = DragUtil.getDisplayBounds(targetPart.getControl());
} else {
// targetBounds = DragUtil.getDisplayBounds(getParent());
targetDisplayBounds = DragUtil.getDisplayBounds(container);
}
if(side == SWT.CENTER || side == SWT.NONE) {
return targetDisplayBounds;
}
int distance = Geometry.getDimension(targetDisplayBounds, !Geometry.isHorizontal(side));
return Geometry.getExtrudedEdge(targetDisplayBounds, (int)(distance * getDockingRatio(sourcePart, targetPart)), side);
}
protected float getDockingRatio(AbstractPart dragged, AbstractPart target) {
return 0.5f;
}
}
/**
* @return the lifeCycleEventProvider
*/
protected SashContainerEventsProvider getLifeCycleEventProvider() {
return lifeCycleEventProvider;
}
/**
* Add a listener on pageChanged event.
* This implementation delegates to the internal PageTracker.
*
* @see org.eclipse.papyrus.infra.core.sasheditor.editor.ISashWindowsContainer#addPageChangedListener(org.eclipse.papyrus.infra.core.sasheditor.editor.IPageChangedListener)
* @param pageChangedListener
*
*/
public void addPageChangedListener(IPageChangedListener pageChangedListener) {
activePageTracker.addPageChangedListener(pageChangedListener);
}
/**
* Remove a listener on pageChanged event.
*
* @see org.eclipse.papyrus.infra.core.sasheditor.editor.ISashWindowsContainer#removePageChangedListener(org.eclipse.papyrus.infra.core.sasheditor.editor.IPageChangedListener)
* @param pageChangedListener
*
*/
public void removePageChangedListener(IPageChangedListener pageChangedListener) {
activePageTracker.removePageChangedListener(pageChangedListener);
}
/**
* Add a listener on Page LifeCycle events.
* This implementation delegates to the internal PageTracker.
*
* @see org.eclipse.papyrus.infra.core.sasheditor.editor.ISashWindowsContainer#addPageChangedListener(org.eclipse.papyrus.infra.core.sasheditor.editor.IPageChangedListener)
* @param listener
*
*/
public void addPageLifeCycleListener(IPageLifeCycleEventsListener listener) {
lifeCycleEventProvider.addListener(listener);
}
/**
* Remove a listener on Page LifeCycle events.
*
* @see org.eclipse.papyrus.infra.core.sasheditor.editor.ISashWindowsContainer#removePageChangedListener(org.eclipse.papyrus.infra.core.sasheditor.editor.IPageChangedListener)
* @param listener
*
*/
public void removePageLifeCycleListener(IPageLifeCycleEventsListener listener) {
lifeCycleEventProvider.removeListener(listener);
}
/* ***************************************************** */
/* Internal Visitors */
/* ***************************************************** */
/**
* Inner class.
* A visitor setting the {@link MenuManager} on each folder.
*/
private class SetFolderTabMenuVisitor extends PartVisitor {
private MenuManager menuManager;
/**
* Constructor.
*
* @param menuManager
*/
public SetFolderTabMenuVisitor(MenuManager menuManager) {
this.menuManager = menuManager;
}
/**
* Set the menu if the visited node is a folder.
*/
@Override
public boolean accept(TabFolderPart part) {
part.setFolderTabMenuManager(menuManager);
return true;
}
}
/**
* Inner class.
* A visitor used to collect all visible page in the sashcontainer.
* A visible page is a page whose the diagram area is visible.
*/
private class CollectVisiblePageVisitor extends PartVisitor {
private List<IPage> visiblePages = new ArrayList<IPage>();
private Class<? extends IPage> expectedClass;
/**
* Constructor.
*
* @param menuManager
*/
public CollectVisiblePageVisitor() {
}
/**
* Constructor.
*
* @param menuManager
*/
@SuppressWarnings("unused")
public CollectVisiblePageVisitor(Class<? extends IPage> expectedClass) {
this.expectedClass = expectedClass;
}
/**
* Get the result list.
* @param <T>
* @return
*/
@SuppressWarnings("unchecked")
public <T> List<T> getVisiblePages() {
return (List<T>)visiblePages;
}
/**
* Set the menu if the visited node is a folder.
*/
@Override
public boolean accept(TabFolderPart part) {
IPage page = part.getVisiblePagePart();
if( part != null) {
if( expectedClass != null && expectedClass.isInstance(page)) {
visiblePages.add(page);
}
else {
visiblePages.add(page);
}
}
return true;
}
}
/**
* Inner class.
* A visitor used to collect all visible page in the sashcontainer.
* A visible page is a page whose the diagram area is visible.
*/
private class AbstractCollectIEditorPart extends PartVisitor {
protected List<IEditorPart> editorParts = new ArrayList<IEditorPart>();
/**
* Constructor.
*
* @param menuManager
*/
public AbstractCollectIEditorPart() {
}
/**
* Get the result list.
* @param <T>
* @return
*/
public List<IEditorPart> getVisiblePages() {
return editorParts;
}
}
/**
* Inner class.
* A visitor used to collect all visible page in the sashcontainer.
* A visible page is a page whose the diagram area is visible.
*/
private class CollectVisibleIEditorPart extends AbstractCollectIEditorPart {
/**
* Set the menu if the visited node is a folder.
*/
@Override
public boolean accept(TabFolderPart part) {
IPage page = part.getVisiblePagePart();
if( page != null && page instanceof IEditorPage ) {
IEditorPage editorPage = (IEditorPage) page;
editorParts.add(editorPage.getIEditorPart());
}
// continue searching
return true;
}
}
/**
* Inner class.
* A visitor used to collect all parts in the sashcontainer.
* A visible page is a page whose the diagram area is visible.
*/
@SuppressWarnings("unused")
private class CollectIEditorParts extends AbstractCollectIEditorPart {
/**
* Add the part to thecollection.
*/
@Override
public boolean accept( EditorPart part) {
IEditorPart editorPart = part.getIEditorPart();
if( editorPart != null ) {
editorParts.add(editorPart);
}
// continue searching
return true;
}
}
}