All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.ui.internal.TrimDropTarget Maven / Gradle / Ivy

Go to download

This plug-in contains the bulk of the Workbench implementation, and depends on JFace, SWT, and Core Runtime. It cannot be used independently from org.eclipse.ui. Workbench client plug-ins should not depend directly on this plug-in.

The newest version!
/*******************************************************************************
 * Copyright (c) 2004, 2007 IBM Corporation and others.
 * 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.ui.internal;

import java.util.Iterator;
import java.util.List;

import org.eclipse.jface.util.Geometry;
import org.eclipse.swt.SWT;
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.internal.dnd.DragBorder;
import org.eclipse.ui.internal.dnd.DragUtil;
import org.eclipse.ui.internal.dnd.IDragOverListener;
import org.eclipse.ui.internal.dnd.IDropTarget;
import org.eclipse.ui.internal.dnd.IDropTarget2;
import org.eclipse.ui.internal.layout.IWindowTrim;
import org.eclipse.ui.internal.layout.LayoutUtil;
import org.eclipse.ui.internal.layout.TrimDescriptor;
import org.eclipse.ui.internal.layout.TrimLayout;
import org.eclipse.ui.internal.layout.TrimToolBarBase;

/**
 */
/*package*/class TrimDropTarget implements IDragOverListener {
	
    private final class ActualTrimDropTarget implements IDropTarget2 {
        public IWindowTrim draggedTrim;
        
        // tracking parameters
    	private DragBorder border = null;
    	private int dockedArea;
        
        // Holder for the position of trim that is 'floating' with the cursor
    	private int cursorAreaId;
        private int initialAreaId;
        private IWindowTrim initialInsertBefore;        
		private Rectangle initialLocation;

        /**
         * Constructor
         */
        private ActualTrimDropTarget() {
            super();

            draggedTrim = null;
            dockedArea = SWT.NONE;
            
            initialAreaId = SWT.NONE;
            initialInsertBefore = null;
        }
        
        /**
         * This method is used to delineate separate trims dragging events. The -first- drag
         * event will set this and then it will remain constant until the drag gesture is done;
         * either by dropping or escaping. Once the gesture is finished the trim value is set
         * back to 'null'.
         * 
         * @param trim The trim item currently being dragged.
         */
        public void startDrag(IWindowTrim trim) {
        	// Are we starting a new drag?
        	if (draggedTrim != trim) {
            	// remember the dragged trim
            	draggedTrim = trim;
            	
            	// Remember the location that we were in initially so we
            	// can go back there on an cancel...
            	initialAreaId = layout.getTrimAreaId(draggedTrim.getControl());
            	
            	// Determine who we were placed 'before' in the trim
            	initialInsertBefore = getInsertBefore(initialAreaId, draggedTrim);
            	
            	// Remember the location that the control used to be at for animation purposes
            	initialLocation = DragUtil.getDisplayBounds(draggedTrim.getControl());
            	            	
            	// The dragged trim is always initially docked
            	dockedArea = initialAreaId;
        	}
        }
                
        /**
         * Determine the trim area from the point. To avoid clashing at the 'corners' due to extending the trim area's
         * rectangles we first ensure that the point is not actually -within- a trim area before we check the extended
         * rectangles.
         * 
         * @param pos The current cursor pos
         * @return the Trim area that the cursor is in or SWT.NONE if the point is not in an area
         */
        private int getTrimArea(Point pos) {
        	// First, check if we're actually -within- a trim area (i.e. no boundary extensions)
        	int areaId = getTrimArea(pos, 0);
        	
        	// If we are not inside a trim area...are we 'close' to one?
        	if (areaId == SWT.NONE) {
				areaId = getTrimArea(pos, TrimDragPreferences.getThreshold());
			}
        	
        	// not inside any trim area
        	return areaId;
        }
        
        /**
         * Checks the trims areas against the given position. Each trim area is 'extended' into
         * the workbench page by the value of extendedBoundaryWidth before the checking
         * takes place.
         * 
         * @param pos The point to check against
         * @param extendedBoundaryWidth The amount to extend the trim area's 'inner' edge by
         * 
         * @return The trim area or SWT.NONE if the point is not within any extended trim area's rect.
         */
        private int getTrimArea(Point pos, int extendedBoundaryWidth) {
        	int[] areaIds = layout.getAreaIds();
        	for (int i = 0; i < areaIds.length; i++) {
				Rectangle trimRect = layout.getTrimRect(windowComposite, areaIds[i]);
				trimRect = Geometry.toControl(windowComposite, trimRect);

				// Only check 'valid' sides
				if ( (areaIds[i] & getValidSides()) != SWT.NONE) {
					// TODO: more confusion binding 'areaIds' to SWT 'sides'
		        	switch (areaIds[i]) {
					case SWT.LEFT:
						trimRect.width += extendedBoundaryWidth;
						
						if (pos.y >= trimRect.y &&
							pos.y <= (trimRect.y+trimRect.height) &&
							pos.x <= (trimRect.x+trimRect.width)) {
							return areaIds[i];
						}
						break;
					case SWT.RIGHT:
						trimRect.x -= extendedBoundaryWidth;
						trimRect.width += extendedBoundaryWidth;
						
						if (pos.y >= trimRect.y &&
							pos.y <= (trimRect.y+trimRect.height) &&
							pos.x >= trimRect.x) {
							return areaIds[i];
						}
						break;
					case SWT.TOP:
						trimRect.height += extendedBoundaryWidth;
						
						if (pos.x >= trimRect.x &&
							pos.x <= (trimRect.x+trimRect.width) &&
							pos.y <= (trimRect.y+trimRect.height)) {
							return areaIds[i];
						}
						break;
					case SWT.BOTTOM:
						trimRect.y -= extendedBoundaryWidth;
						trimRect.height += extendedBoundaryWidth;
						
						if (pos.x >= trimRect.x &&
							pos.x <= (trimRect.x+trimRect.width) &&
							pos.y >= trimRect.y) {
							return areaIds[i];
						}
						break;
		        	}
				}
			}
        	
        	// not inside any trim area
        	return SWT.NONE;
        }
        
        /**
         * Determine the window trim that the currently dragged trim should be inserted
         * before.
         * @param areaId The area id that is being checked
         * @param pos The position used to determine the correct insertion trim
         * @return The trim to 'dock' the draggedTrim before
         */
        private IWindowTrim getInsertBefore(int areaId, Point pos) {
        	boolean isHorizontal = (areaId == SWT.TOP) || (areaId == SWT.BOTTOM);
        	
        	// Walk the trim area and return the first one that the positon
        	// is 'after'.
        	List tDescs = layout.getTrimArea(areaId).getDescriptors();
        	for (Iterator iter = tDescs.iterator(); iter.hasNext();) {
				TrimDescriptor desc = (TrimDescriptor) iter.next();
				
				// Skip ourselves
				if (desc.getTrim() == draggedTrim) {
					continue;
				}
				
				// Now, check
				Rectangle bb = desc.getCache().getControl().getBounds();
				Point center = Geometry.centerPoint(bb);
				if (isHorizontal) {
					if (pos.x < center.x) {
						return desc.getTrim();
					}
				}
				else {
					if (pos.y < center.y) {
						return desc.getTrim();
					}
				}
			}
        	
        	return null;
        }
        
        /**
         * Returns the trim that is 'before' the given trim in the given area
         * 
         * @param areaId The areaId of the trim
         * @param trim The trim to find the element after
         * 
         * @return The trim that the given trim is 'before'
         */
        private IWindowTrim getInsertBefore(int areaId, IWindowTrim trim) {
        	List tDescs = layout.getTrimArea(areaId).getDescriptors();
        	for (Iterator iter = tDescs.iterator(); iter.hasNext();) {
				TrimDescriptor desc = (TrimDescriptor) iter.next();
				if (desc.getTrim() == trim) {
					if (iter.hasNext()) {
						desc = (TrimDescriptor) iter.next();
						return desc.getTrim();
					}
					return null;
				}
			}
        	
        	return null;
        }
        
        /**
         * Recalculates the drop information based on the current cursor pos.
         * 
         * @param pos The cursor position
         */
        public void track(Point pos) {
        	// Convert the mouse positon into 'local' coords
        	Rectangle r = new Rectangle(pos.x, pos.y, 1,1);
        	r = Geometry.toControl(windowComposite, r);
        	pos.x = r.x;
        	pos.y = r.y;
        	        	
        	// Are we 'inside' a trim area ?
        	cursorAreaId = getTrimArea(pos);

        	// Provide tracking for the appropriate 'mode'
        	if (cursorAreaId != SWT.NONE) {
				trackInsideTrimArea(pos);
			} else {
				trackOutsideTrimArea(pos);
			}
        }
       
        /**
         * Perform the feedback used when the cursor is 'inside' a particular trim area.
         * The current implementation will place the dragged trim into the trim area at
         * the location determined by the supplied point.
         * 
         * @param pos The point to use to determine where in the trim area the dragged trim
         * should be located.
         */
        private void trackInsideTrimArea(Point pos) {
        	// Where should we be?
        	int newArea = getTrimArea(pos);
        	IWindowTrim newInsertBefore = getInsertBefore(newArea, pos);

        	// if we're currently undocked then we should dock
        	boolean shouldDock = dockedArea == SWT.NONE;
        	
        	// If we're already docked then only update if there's a change in area or position
        	if (dockedArea != SWT.NONE) {
	        	// Where are we now?
	        	IWindowTrim curInsertBefore = getInsertBefore(dockedArea, draggedTrim);
	        	
	        	// If we're already docked we should only update if there's a change
	        	shouldDock = dockedArea != newArea || curInsertBefore != newInsertBefore;
        	}
        	
        	// Do we have to do anything?
        	if (shouldDock) {
        		// (Re)dock the trim in the new location
        		dock(newArea, newInsertBefore);
        	}
        }
        
        /**
         * Provide the dragging feedback when the cursor is -not- explicitly inside
         * a particular trim area.
         * 
         */
        private void trackOutsideTrimArea(Point pos) {
        	// If we -were- docked then undock
        	if (dockedArea != SWT.NONE) {
				undock();
			}
    		
    		border.setLocation(pos, SWT.BOTTOM);
        }
                
        /**
         * Return the set of valid sides that a piece of trim can be docked on. We
         * arbitrarily extend this to include any areas that won't cause a change in orientation
         * 
         * @return The extended drop 'side' set
         */
        private int getValidSides() {
        	int result = draggedTrim.getValidSides();
        	if (result == SWT.NONE) {
				return result;
			}

        	// For now, if we can dock...we can dock -anywhere-
        	return SWT.TOP | SWT.BOTTOM | SWT.LEFT | SWT.RIGHT;
        }

        /**
         * The user either cancelled the drag or tried to drop the trim in an invalid
         * area...put the trim back in the last location it was in
         */
        private void redock() {
        	// Since the control might move 'far' we'll provide an animation
        	Rectangle startRect = DragUtil.getDisplayBounds(draggedTrim.getControl());
            RectangleAnimation animation = new RectangleAnimation(
                    windowComposite.getShell(), startRect, initialLocation, 300);
            animation.schedule();

            dock(initialAreaId, initialInsertBefore);
        }
        
		/* (non-Javadoc)
         * @see org.eclipse.ui.internal.dnd.IDropTarget#drop()
         */
        public void drop() {
        	// If we aren't docked then restore the initial location
        	if (dockedArea == SWT.NONE) {
				redock();
			}
        }

        /**
         * Remove the trim frmo its current 'docked' location and attach it
         * to the cursor...
         */
        private void undock() {
        	// Remove the trim from the layout
        	layout.removeTrim(draggedTrim);
           	LayoutUtil.resize(draggedTrim.getControl());
           	
           	// Re-orient the widget to its -original- side and size
        	draggedTrim.dock(initialAreaId);
        	draggedTrim.getControl().setSize(initialLocation.width, initialLocation.height);
           	
           	// Create a new dragging border onto the dragged trim
        	// Special check for TrimPart...should be generalized
        	boolean wantsFrame = !(draggedTrim instanceof TrimToolBarBase);
           	border = new DragBorder(windowComposite, draggedTrim.getControl(), wantsFrame);

           	dockedArea = SWT.NONE;
        }
        
        /**
         * Return the 'undocked' trim to its previous location in the layout
         */
        private void dock(int areaId, IWindowTrim insertBefore) {
        	// remove the drag 'border'
        	if (border != null) {
				border.dispose();
				border = null;
        	}
			
			// Update the trim's orientation if necessary
			draggedTrim.dock(areaId);

			// Add the trim into the layout
            layout.addTrim(areaId, draggedTrim, insertBefore);
           	LayoutUtil.resize(draggedTrim.getControl());
           	
           	// Remember the area that we're currently docked in
           	dockedArea = areaId;
        }
        	
        /* (non-Javadoc)
         * @see org.eclipse.ui.internal.dnd.IDropTarget#getCursor()
         */
        public Cursor getCursor() {
        	// If the trim isn't docked then show the 'no smoking' sign
        	if (cursorAreaId == SWT.NONE) {
				return windowComposite.getDisplay().getSystemCursor(SWT.CURSOR_NO);
			}
        	
        	// It's docked; show the four-way arrow cursor
        	return windowComposite.getDisplay().getSystemCursor(SWT.CURSOR_SIZEALL);
        }

        /* (non-Javadoc)
         * @see org.eclipse.ui.internal.dnd.IDropTarget#getSnapRectangle()
         */
        public Rectangle getSnapRectangle() {
        	// TODO: KLUDGE!! We don't want to show -any- snap rect
        	// but Tracker won't allow that so place it where it won't be visible
        	return new Rectangle(100000, 0,0,0);
        }

		/* (non-Javadoc)
		 * @see org.eclipse.ui.internal.dnd.IDropTarget2#dragFinished(boolean)
		 */
		public void dragFinished(boolean dropPerformed) {
			// If we didn't perform a drop then restore the original position
			if (!dropPerformed && dockedArea == SWT.NONE) {
				// Force the dragged trim back into its original position...				
				redock();
			}
			
			// Set the draggedTrim to null. This indicates that we're no longer
			// dragging the trim. The first call to the TrimDropTarget's 'drag' method
			// will reset this the next time a drag starts.
			draggedTrim = null;
		}
    }
    
    private ActualTrimDropTarget dropTarget;
    
    private TrimLayout layout;
    private Composite windowComposite;

    /**
     * Create a new drop target capable of accepting IWindowTrim items
     * 
     * @param someComposite The control owning the TrimLayout
     * @param theWindow the workbenchWindow
     */
    public TrimDropTarget(Composite someComposite, WorkbenchWindow theWindow) {
        layout = (TrimLayout) someComposite.getLayout();
        windowComposite = someComposite;

        // Create an instance of a drop target to use
        dropTarget = new ActualTrimDropTarget();
    }

    /* (non-Javadoc)
     * @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, final Rectangle dragRectangle) {
    	
    	// Have to be dragging trim
    	if (!(draggedObject instanceof IWindowTrim)) {
			return null;
		}
    	
    	// OK, we're dragging trim. is it from -this- shell?
    	IWindowTrim trim = (IWindowTrim) draggedObject;
    	if (trim.getControl().getShell() != windowComposite.getShell()) {
			return null;
		}
    	
    	// If this is the -first- drag then inform the drop target
    	if (dropTarget.draggedTrim == null) {
    		dropTarget.startDrag(trim);
    	}

    	// Forward on to the 'actual' drop target for feedback
    	dropTarget.track(position);
    	
    	// Spin the paint loop after every track
    	windowComposite.getDisplay().update();
    	
		return dropTarget;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy