0001: /*******************************************************************************
0002: * Copyright (c) 2004, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.ui.internal.layout;
0011:
0012: import java.util.ArrayList;
0013: import java.util.Collections;
0014: import java.util.HashMap;
0015: import java.util.Iterator;
0016: import java.util.List;
0017: import java.util.Map;
0018:
0019: import org.eclipse.jface.preference.IPreferenceStore;
0020: import org.eclipse.jface.util.Geometry;
0021: import org.eclipse.swt.SWT;
0022: import org.eclipse.swt.custom.CBanner;
0023: import org.eclipse.swt.events.DisposeEvent;
0024: import org.eclipse.swt.events.DisposeListener;
0025: import org.eclipse.swt.graphics.Point;
0026: import org.eclipse.swt.graphics.Rectangle;
0027: import org.eclipse.swt.widgets.Composite;
0028: import org.eclipse.swt.widgets.Control;
0029: import org.eclipse.swt.widgets.Layout;
0030: import org.eclipse.ui.IWorkbenchPreferenceConstants;
0031: import org.eclipse.ui.PlatformUI;
0032: import org.eclipse.ui.internal.TrimDragPreferences;
0033:
0034: /**
0035: * Lays out the children of a Composite. One control occupies the center of the
0036: * composite, and any number of controls may be attached to the top, bottom, and
0037: * sides. This is a common layout for application windows, which typically have
0038: * a central work area and a number of small widgets (toolbars, status lines,
0039: * etc.) attached to the edge.
0040: *
0041: * <p>
0042: * Unlike most other SWT layouts, this layout does not require layout data to be
0043: * attached to each child control. Instead, member functions on the Layout are
0044: * used to control the positioning of controls within the layout.
0045: * </p>
0046: *
0047: * <p>
0048: * The interface to this layout is intended to easily support drag-and-drop.
0049: * Trim widgets can be added, removed, or inserted between other existing
0050: * widgets and the layout will adjust accordingly. If one side of the layout
0051: * contains no trim widgets, the central area will expand to reclaim the unused
0052: * space.
0053: * </p>
0054: *
0055: * <p>
0056: * This layout must be told about every widget that it is supposed to arrange.
0057: * If the composite contains additional widgets, they will not be moved by the
0058: * layout and may be arranged through other means.
0059: * </p>
0060: *
0061: * @since 3.0
0062: */
0063: public class TrimLayout extends Layout implements ICachingLayout,
0064: ITrimManager {
0065:
0066: /**
0067: * Trim area ID.
0068: */
0069: public static final Integer TOP_ID = new Integer(TOP);
0070:
0071: /**
0072: * Trim area ID.
0073: */
0074: public static final Integer BOTTOM_ID = new Integer(BOTTOM);
0075:
0076: /**
0077: * Trim area ID.
0078: */
0079: public static final Integer LEFT_ID = new Integer(LEFT);
0080:
0081: /**
0082: * Trim area ID.
0083: */
0084: public static final Integer RIGHT_ID = new Integer(RIGHT);
0085:
0086: /**
0087: * Trim area ID.
0088: */
0089: public static final Integer NONTRIM_ID = new Integer(NONTRIM);
0090:
0091: /**
0092: * IDs for the current trim areas we support.
0093: */
0094: private static final int[] TRIM_ID_INFO = { LEFT, RIGHT, TOP,
0095: BOTTOM };
0096:
0097: private SizeCache centerArea = new SizeCache();
0098:
0099: /**
0100: * Map of TrimAreas by IDs.
0101: */
0102: private Map fTrimArea = new HashMap();
0103:
0104: /**
0105: * Map of TrimDescriptors by IDs.
0106: */
0107: private Map fTrimDescriptors = new HashMap();
0108:
0109: private int marginWidth;
0110:
0111: private int marginHeight;
0112:
0113: private int topSpacing;
0114:
0115: private int bottomSpacing;
0116:
0117: private int leftSpacing;
0118:
0119: private int rightSpacing;
0120:
0121: private int spacing = 3;
0122:
0123: private boolean trimLocked;
0124:
0125: private HashMap preferredLocationMap = new HashMap();
0126:
0127: /**
0128: * Creates a new (initially empty) trim layout.
0129: */
0130: public TrimLayout() {
0131: // Determine whether or not the trim is 'locked'
0132: final IPreferenceStore store = PlatformUI.getPreferenceStore();
0133: trimLocked = store
0134: .getBoolean(IWorkbenchPreferenceConstants.LOCK_TRIM);
0135:
0136: createTrimArea(TOP_ID, TOP_ID.toString(), SWT.DEFAULT, SWT.TOP);
0137: createTrimArea(BOTTOM_ID, BOTTOM_ID.toString(), SWT.DEFAULT,
0138: SWT.BOTTOM);
0139: createTrimArea(LEFT_ID, LEFT_ID.toString(), SWT.DEFAULT,
0140: SWT.LEFT);
0141: createTrimArea(RIGHT_ID, RIGHT_ID.toString(), SWT.DEFAULT,
0142: SWT.RIGHT);
0143: }
0144:
0145: private void createTrimArea(Integer id, String displayName,
0146: int trimSize, int trimMods) {
0147: TrimArea top = new TrimArea(id.intValue(), displayName);
0148: top.setTrimSize(trimSize);
0149: top.setControlModifiers(trimMods);
0150: fTrimArea.put(id, top);
0151: }
0152:
0153: /**
0154: * Sets the empty space surrounding the center area. This whitespace is
0155: * located between the trim and the central widget.
0156: *
0157: * @param left
0158: * pixel width
0159: * @param right
0160: * pixel width
0161: * @param top
0162: * pixel width
0163: * @param bottom
0164: * pixel width
0165: */
0166: public void setSpacing(int left, int right, int top, int bottom) {
0167: leftSpacing = left;
0168: rightSpacing = right;
0169: topSpacing = top;
0170: bottomSpacing = bottom;
0171: }
0172:
0173: /**
0174: * Sets the empty space around the outside of the layout. This whitespace is
0175: * located outside the trim widgets.
0176: *
0177: * @param marginWidth
0178: * @param marginHeight
0179: */
0180: public void setMargins(int marginWidth, int marginHeight) {
0181: this .marginWidth = marginWidth;
0182: this .marginHeight = marginHeight;
0183: }
0184:
0185: /**
0186: * Sets the trimSize (pixels) for the given side of the layout. If
0187: * SWT.DEFAULT, then the trim size will be computed from child controls.
0188: *
0189: * @param areaId
0190: * the area ID
0191: * @param size
0192: * in pixels
0193: * @see #getAreaIds()
0194: */
0195: public void setTrimSize(int areaId, int size) {
0196: TrimArea area = (TrimArea) fTrimArea.get(new Integer(areaId));
0197: if (area != null) {
0198: area.setTrimSize(size);
0199: }
0200: }
0201:
0202: /**
0203: * Returns the location of the given trim control. For example, returns
0204: * SWT.LEFT if the control is docked on the left, SWT.RIGHT if docked on the
0205: * right, etc. Returns SWT.DEFAULT if the given control is not a trim
0206: * control.
0207: *
0208: * @param trimControl
0209: * control to query
0210: * @return The area ID of this control. If the control is not part of our
0211: * trim, return SWT.DEFAULT.
0212: * @see #getAreaIds()
0213: */
0214: public int getTrimAreaId(Control trimControl) {
0215: TrimDescriptor desc = findTrimDescription(trimControl);
0216: if (desc != null) {
0217: return desc.getAreaId();
0218: }
0219: return SWT.DEFAULT;
0220: }
0221:
0222: /**
0223: * @param control
0224: * new window trim to be added
0225: * @param areaId
0226: * the area ID
0227: * @see #getAreaIds()
0228: * @deprecated
0229: */
0230: public void addTrim(IWindowTrim control, int areaId) {
0231: addTrim(areaId, control, null);
0232: }
0233:
0234: /**
0235: *
0236: * @param trim
0237: * new window trim to be added
0238: * @param areaId
0239: * the area ID
0240: * @param beforeMe
0241: * if null, the control will be inserted as the last trim widget
0242: * on this side of the layout. Otherwise, the control will be
0243: * inserted before the given widget.
0244: * @see #getAreaIds()
0245: * @deprecated
0246: */
0247: public void addTrim(IWindowTrim trim, int areaId,
0248: IWindowTrim beforeMe) {
0249: addTrim(areaId, trim, beforeMe);
0250: }
0251:
0252: /*
0253: * (non-Javadoc)
0254: *
0255: * @see org.eclipse.ui.internal.layout.ITrimManager#addTrim(int,
0256: * org.eclipse.ui.internal.IWindowTrim)
0257: */
0258: public void addTrim(int areaId, IWindowTrim trim) {
0259: // If we're adding trim to the same side that it's
0260: // already on then don't change its order
0261: IWindowTrim insertBefore = null;
0262: List trimDescs = getAreaTrim(areaId);
0263: for (Iterator trimIter = trimDescs.iterator(); trimIter
0264: .hasNext();) {
0265: IWindowTrim curTrim = (IWindowTrim) trimIter.next();
0266: if (curTrim.getId().equals(trim.getId())) {
0267: if (trimIter.hasNext()) {
0268: insertBefore = (IWindowTrim) trimIter.next();
0269: }
0270: }
0271: }
0272:
0273: addTrim(areaId, trim, insertBefore);
0274: }
0275:
0276: /*
0277: * (non-Javadoc)
0278: *
0279: * @see org.eclipse.ui.internal.layout.ITrimManager#addTrim(int,
0280: * org.eclipse.ui.internal.IWindowTrim,
0281: * org.eclipse.ui.internal.IWindowTrim)
0282: */
0283: public void addTrim(int areaId, IWindowTrim trim,
0284: IWindowTrim beforeMe) {
0285: TrimArea area = (TrimArea) fTrimArea.get(new Integer(areaId));
0286: if (area == null) {
0287: return;
0288: }
0289:
0290: // remove the trim from the current layout
0291: removeTrim(trim);
0292:
0293: // Create a new trim descriptor for the new area...
0294: TrimDescriptor desc = new TrimDescriptor(trim, areaId);
0295:
0296: // If the trim can be relocated then add a docking handle
0297: boolean isAlreadyAHandle = trim instanceof TrimToolBarBase;
0298: if (!trimLocked && trim.getValidSides() != SWT.NONE
0299: && !isAlreadyAHandle) {
0300: // Create a 'docking' handle to allow dragging the trim
0301: Composite dockingHandle = new TrimCommonUIHandle(this ,
0302: trim, areaId);
0303: desc.setDockingCache(new SizeCache(dockingHandle));
0304: }
0305:
0306: // Add the trim control
0307: SizeCache cache = new SizeCache(trim.getControl());
0308: trim.getControl().setLayoutData(trim);
0309: desc.setCache(cache);
0310:
0311: // Add a dispose listener so we can clean up if the Client disposes the trim
0312: trim.getControl().addDisposeListener(new DisposeListener() {
0313: public void widgetDisposed(DisposeEvent e) {
0314: Control control = (Control) e.widget;
0315: if (control.getLayoutData() instanceof IWindowTrim) {
0316: IWindowTrim trim = (IWindowTrim) control
0317: .getLayoutData();
0318: removeTrim(trim);
0319: //forceLayout();
0320: }
0321: }
0322: });
0323:
0324: // Add the new descriptor to the map
0325: fTrimDescriptors.put(desc.getId(), desc);
0326:
0327: // insert before behaviour, revisited
0328: if (beforeMe != null) {
0329: TrimDescriptor beforeDesc = (TrimDescriptor) fTrimDescriptors
0330: .get(beforeMe.getId());
0331: if (beforeDesc != null && beforeDesc.getAreaId() == areaId) {
0332: area.addTrim(desc, beforeDesc);
0333: } else {
0334: area.addTrim(desc);
0335: }
0336: } else {
0337: area.addTrim(desc);
0338: }
0339: }
0340:
0341: /**
0342: * Force a layout of the trim
0343: */
0344: public void forceLayout() {
0345: removeDisposed();
0346:
0347: // we hack this by calling the LayoutUtil with the
0348: // first piece of trim that we find...(kludge!!)
0349: Iterator d = fTrimDescriptors.values().iterator();
0350: while (d.hasNext()) {
0351: TrimDescriptor desc = (TrimDescriptor) d.next();
0352: if (desc.getTrim().getControl() != null) {
0353: LayoutUtil.resize(desc.getTrim().getControl());
0354: return;
0355: }
0356: }
0357: }
0358:
0359: /*
0360: * (non-Javadoc)
0361: *
0362: * @see org.eclipse.ui.internal.layout.ITrimManager#removeTrim(org.eclipse.ui.internal.IWindowTrim)
0363: */
0364: public void removeTrim(IWindowTrim toRemove) {
0365: TrimDescriptor desc = (TrimDescriptor) fTrimDescriptors
0366: .remove(toRemove.getId());
0367: if (desc == null) {
0368: return;
0369: }
0370:
0371: TrimArea area = (TrimArea) fTrimArea.get(new Integer(desc
0372: .getAreaId()));
0373: if (area != null) {
0374: area.removeTrim(desc);
0375: desc.getCache().getControl().setLayoutData(null);
0376: }
0377:
0378: // If we had a trim UI handle then dispose it
0379: if (desc.getDockingCache() != null) {
0380: Control ctrl = desc.getDockingCache().getControl();
0381:
0382: // KLUDGE!! we'll leak a handle rather than losing the
0383: // mouse capture (for now...)
0384: ctrl.setVisible(false);
0385: //ctrl.dispose();
0386: desc.setDockingCache(null);
0387: }
0388: }
0389:
0390: /*
0391: * (non-Javadoc)
0392: *
0393: * @see org.eclipse.ui.internal.layout.ITrimManager#getTrim(java.lang.String)
0394: */
0395: public IWindowTrim getTrim(String id) {
0396: TrimDescriptor desc = (TrimDescriptor) fTrimDescriptors.get(id);
0397: if (desc != null) {
0398: return desc.getTrim();
0399: }
0400: return null;
0401: }
0402:
0403: /**
0404: * Removes any disposed widgets from this layout. This is still experimental
0405: * code.
0406: */
0407: private void removeDisposed() {
0408: Iterator a = fTrimArea.values().iterator();
0409: while (a.hasNext()) {
0410: TrimArea area = (TrimArea) a.next();
0411: Iterator d = area.getDescriptors().iterator();
0412: while (d.hasNext()) {
0413: TrimDescriptor desc = (TrimDescriptor) d.next();
0414: Control nextControl = desc.getTrim().getControl();
0415: if (nextControl == null || nextControl.isDisposed()) {
0416: // Remvoe the trim from the area's list (not the local copy)
0417: area.removeTrim(desc);
0418:
0419: // Remove it from the map
0420: fTrimDescriptors.remove(desc.getId());
0421: }
0422: }
0423: }
0424: }
0425:
0426: /*
0427: * (non-Javadoc)
0428: *
0429: * @see org.eclipse.swt.widgets.Layout#computeSize(org.eclipse.swt.widgets.Composite,
0430: * int, int, boolean)
0431: */
0432: protected Point computeSize(Composite composite, int wHint,
0433: int hHint, boolean flushCache) {
0434: Point result = new Point(wHint, hHint);
0435:
0436: TrimArea top = (TrimArea) fTrimArea.get(TOP_ID);
0437: TrimArea bottom = (TrimArea) fTrimArea.get(BOTTOM_ID);
0438: TrimArea left = (TrimArea) fTrimArea.get(LEFT_ID);
0439: TrimArea right = (TrimArea) fTrimArea.get(RIGHT_ID);
0440:
0441: int horizontalTrim = left.calculateTrimSize(wHint, hHint)
0442: + right.calculateTrimSize(wHint, hHint)
0443: + (2 * marginWidth) + leftSpacing + rightSpacing;
0444: int verticalTrim = top.calculateTrimSize(wHint, hHint)
0445: + bottom.calculateTrimSize(wHint, hHint)
0446: + (2 * marginHeight) + topSpacing + bottomSpacing;
0447:
0448: Point innerSize = centerArea.computeSize(
0449: wHint == SWT.DEFAULT ? wHint : wHint - horizontalTrim,
0450: hHint == SWT.DEFAULT ? hHint : hHint - verticalTrim);
0451:
0452: if (wHint == SWT.DEFAULT) {
0453: result.x = innerSize.x + horizontalTrim;
0454: } else if (hHint == SWT.DEFAULT) {
0455: result.y = innerSize.y + verticalTrim;
0456: }
0457:
0458: /*
0459: * We -can't- determine the correct size for the shell because
0460: * of it's underlying 'big lie' structure and the fact that most of the
0461: * UI elements don't really exist until shown (meaning that
0462: * the normal 'computeSize' mechanism won't work).
0463: *
0464: * See bug 166619 for details but we'll keep returning the
0465: * current value (and the code above for legacy reasons...
0466: */
0467: return new Point(0, 0);
0468: }
0469:
0470: /*
0471: * (non-Javadoc)
0472: *
0473: * @see org.eclipse.swt.widgets.Layout#layout(org.eclipse.swt.widgets.Composite,
0474: * boolean)
0475: */
0476: protected void layout(Composite composite, boolean flushCache) {
0477: removeDisposed();
0478:
0479: TrimArea top = (TrimArea) fTrimArea.get(TOP_ID);
0480: TrimArea bottom = (TrimArea) fTrimArea.get(BOTTOM_ID);
0481: TrimArea left = (TrimArea) fTrimArea.get(LEFT_ID);
0482: TrimArea right = (TrimArea) fTrimArea.get(RIGHT_ID);
0483:
0484: Rectangle clientArea = composite.getClientArea();
0485:
0486: clientArea.x += marginWidth;
0487: clientArea.width -= 2 * marginWidth;
0488: clientArea.y += marginHeight;
0489: clientArea.height -= 2 * marginHeight;
0490:
0491: // Get the max of the trim's preferred size for each side
0492: int trim_top = top.calculateTrimSize(clientArea.width,
0493: clientArea.height);
0494: int trim_bottom = bottom.calculateTrimSize(clientArea.width,
0495: clientArea.height);
0496: int trim_left = left.calculateTrimSize(clientArea.width,
0497: clientArea.height);
0498: int trim_right = right.calculateTrimSize(clientArea.width,
0499: clientArea.height);
0500:
0501: // Determine the amount of 'useable' space for the trim
0502: int leftOfLayout = clientArea.x;
0503: int leftOfCenterPane = leftOfLayout + trim_left + leftSpacing;
0504: int widthOfCenterPane = clientArea.width - trim_left
0505: - trim_right - leftSpacing - rightSpacing;
0506: int rightOfCenterPane = clientArea.x + clientArea.width
0507: - trim_right;
0508:
0509: // HACK!! Surgical Fix: Ideally the fix would handle 'wrapping' trim
0510: // on any side. The following code is in place specifically to
0511: // handle the CBanner's CoolBar wrapping. The code that calculates 'trim_top'
0512: // above won't pick up if the CoolBar wraps and uses extra height
0513: // So we have to re-calc the value based on the actual trim height
0514: // as laid out.
0515:
0516: // Do the layout of the top and update the height to match the actual
0517: // height used
0518: int topOfLayout = clientArea.y;
0519: trim_top = arrange(new Rectangle(leftOfLayout, topOfLayout,
0520: clientArea.width, trim_top), top.getCaches(), !top
0521: .isVertical(), spacing);
0522:
0523: // Now that we have an accurate value for the top's height we can
0524: // proceed with the rest of the layout 'normally'.
0525: int topOfCenterPane = topOfLayout + trim_top + topSpacing;
0526: int heightOfCenterPane = clientArea.height - trim_top
0527: - trim_bottom - topSpacing - bottomSpacing;
0528: int bottomOfCenterPane = clientArea.y + clientArea.height
0529: - trim_bottom;
0530:
0531: arrange(new Rectangle(leftOfCenterPane, bottomOfCenterPane,
0532: widthOfCenterPane, trim_bottom), bottom.getCaches(),
0533: !bottom.isVertical(), spacing);
0534: arrange(new Rectangle(leftOfLayout, topOfCenterPane, trim_left,
0535: clientArea.height - trim_top), left.getCaches(), !left
0536: .isVertical(), spacing);
0537: arrange(new Rectangle(rightOfCenterPane, topOfCenterPane,
0538: trim_right, clientArea.height - trim_top), right
0539: .getCaches(), !right.isVertical(), spacing);
0540:
0541: if (centerArea.getControl() != null) {
0542: centerArea.getControl().setBounds(leftOfCenterPane,
0543: topOfCenterPane, widthOfCenterPane,
0544: heightOfCenterPane);
0545: }
0546: }
0547:
0548: /**
0549: * Arranges all the given controls in a horizontal row that fills the given
0550: * rectangle.
0551: *
0552: * @param area
0553: * area to be filled by the controls
0554: * @param caches
0555: * a list of SizeCaches for controls that will span the rectangle
0556: * @param horizontally
0557: * how we are filling the rectangle
0558: * @param spacing
0559: * in pixels
0560: */
0561: private static int arrange(Rectangle area, List caches,
0562: boolean horizontally, int spacing) {
0563: Point currentPosition = new Point(area.x, area.y);
0564:
0565: List resizable = new ArrayList(caches.size());
0566: List nonResizable = new ArrayList(caches.size());
0567:
0568: TrimArea.filterResizable(caches, resizable, nonResizable,
0569: horizontally);
0570:
0571: int[] sizes = new int[nonResizable.size()];
0572:
0573: int idx = 0;
0574: int used = 0;
0575: int hint = Geometry.getDimension(area, !horizontally);
0576:
0577: // Compute the sizes of non-resizable controls
0578: Iterator iter = nonResizable.iterator();
0579:
0580: while (iter.hasNext()) {
0581: SizeCache next = (SizeCache) iter.next();
0582:
0583: sizes[idx] = TrimArea.getSize(next, hint, horizontally);
0584: used += sizes[idx];
0585: idx++;
0586: }
0587:
0588: int available = Geometry.getDimension(area, horizontally)
0589: - used - spacing * (caches.size() - 1);
0590: idx = 0;
0591: int remainingResizable = resizable.size();
0592:
0593: iter = caches.iterator();
0594:
0595: while (iter.hasNext()) {
0596: SizeCache next = (SizeCache) iter.next();
0597: if (next.getControl().isVisible()) {
0598:
0599: int this Size;
0600: if (TrimArea.isResizable(next.getControl(),
0601: horizontally)) {
0602: this Size = available / remainingResizable;
0603: available -= this Size;
0604: remainingResizable--;
0605: } else {
0606: this Size = sizes[idx];
0607: idx++;
0608: }
0609:
0610: if (TrimDragPreferences.showRaggedTrim()) {
0611: Point prefSize = next.computeSize(SWT.DEFAULT,
0612: SWT.DEFAULT);
0613: if (horizontally) {
0614: // HACK!! Surgical Fix: Ideally the fix would handle 'wrapping' trim
0615: // on any side. The following code is in place specifically to
0616: // handle the CBanner's CoolBar wrapping. We need to pick up
0617: // if the CoolBar wraps and uses extra height
0618: // So we have to re-calc the value based on the actual trim height
0619: // as laid out.
0620: if (next.getControl() instanceof CBanner) {
0621: prefSize = next.getControl().computeSize(
0622: this Size, SWT.DEFAULT);
0623: if (prefSize.y > hint)
0624: hint = prefSize.y;
0625: }
0626:
0627: next.getControl()
0628: .setBounds(currentPosition.x,
0629: currentPosition.y, this Size,
0630: prefSize.y);
0631: currentPosition.x += this Size + spacing;
0632: } else {
0633: next.getControl()
0634: .setBounds(currentPosition.x,
0635: currentPosition.y, prefSize.x,
0636: this Size);
0637: currentPosition.y += this Size + spacing;
0638: }
0639: } else {
0640: if (horizontally) {
0641: next.getControl().setBounds(currentPosition.x,
0642: currentPosition.y, this Size, hint);
0643: currentPosition.x += this Size + spacing;
0644: } else {
0645: next.getControl().setBounds(currentPosition.x,
0646: currentPosition.y, hint, this Size);
0647: currentPosition.y += this Size + spacing;
0648: }
0649: }
0650: }
0651: }
0652:
0653: return hint;
0654: }
0655:
0656: /**
0657: * Sets the widget that will occupy the central area of the layout.
0658: * Typically, this will be a composite that contains the main widgetry of
0659: * the application.
0660: *
0661: * @param center
0662: * control that will occupy the center of the layout, or null if
0663: * none
0664: */
0665: public void setCenterControl(Control center) {
0666: centerArea.setControl(center);
0667: }
0668:
0669: /**
0670: * Returns the control in the center of this layout
0671: *
0672: * @return the center area control.
0673: */
0674: public Control getCenterControl() {
0675: return centerArea.getControl();
0676: }
0677:
0678: /*
0679: * (non-Javadoc)
0680: *
0681: * @see org.eclipse.ui.internal.layout.ICachingLayout#flush(org.eclipse.swt.widgets.Control)
0682: */
0683: public void flush(Control dirtyControl) {
0684: if (dirtyControl == centerArea.getControl()) {
0685: centerArea.flush();
0686: } else {
0687: TrimDescriptor desc = findTrimDescription(dirtyControl);
0688: if (desc != null) {
0689: desc.flush();
0690: }
0691: }
0692: }
0693:
0694: /*
0695: * (non-Javadoc)
0696: *
0697: * @see org.eclipse.ui.internal.layout.ITrimManager#getAreaIds()
0698: */
0699: public int[] getAreaIds() {
0700: return (int[]) TRIM_ID_INFO.clone();
0701: }
0702:
0703: /*
0704: * (non-Javadoc)
0705: *
0706: * @see org.eclipse.ui.internal.layout.ITrimManager#getAreaTrim(int)
0707: */
0708: public List getAreaTrim(int areaId) {
0709: TrimArea area = (TrimArea) fTrimArea.get(new Integer(areaId));
0710: if (area == null) {
0711: return Collections.EMPTY_LIST;
0712: }
0713: return area.getTrims();
0714: }
0715:
0716: /*
0717: * (non-Javadoc)
0718: *
0719: * @see org.eclipse.ui.internal.layout.ITrimManager#updateAreaTrim(int,
0720: * java.util.List, boolean)
0721: */
0722: public void updateAreaTrim(int id, List trim, boolean removeExtra) {
0723: TrimArea area = (TrimArea) fTrimArea.get(new Integer(id));
0724: if (area == null) {
0725: return;
0726: }
0727: List current = area.getTrims();
0728:
0729: // add back the trim ... this takes care of moving it
0730: // from one trim area to another.
0731: Iterator i = trim.iterator();
0732: while (i.hasNext()) {
0733: IWindowTrim t = (IWindowTrim) i.next();
0734: t.dock(id); // Ensure that the trim is properly oriented
0735: addTrim(id, t, null);
0736: current.remove(t);
0737: }
0738:
0739: if (removeExtra) {
0740: // if it wasn't removed from the current list, then it's extra
0741: // trim we don't need.
0742: i = current.iterator();
0743: while (i.hasNext()) {
0744: IWindowTrim t = (IWindowTrim) i.next();
0745: removeTrim(t);
0746: }
0747: }
0748: }
0749:
0750: /**
0751: * Return a trim area rectangle.
0752: *
0753: * @param window
0754: * the window that has the trim
0755: * @param areaId
0756: * the side it's on
0757: * @return the area rectangle.
0758: * @since 3.2
0759: * @see #getAreaIds()
0760: */
0761: public Rectangle getTrimRect(Composite window, int areaId) {
0762: Rectangle bb = window.getBounds();
0763:
0764: Rectangle cr = window.getClientArea();
0765: Rectangle tr = window.computeTrim(cr.x, cr.y, cr.width,
0766: cr.height);
0767:
0768: // Place the Client Area 'within' its window
0769: Geometry.moveRectangle(cr, new Point(bb.x - tr.x, bb.y - tr.y));
0770:
0771: TrimArea top = (TrimArea) fTrimArea.get(TOP_ID);
0772: TrimArea bottom = (TrimArea) fTrimArea.get(BOTTOM_ID);
0773: TrimArea left = (TrimArea) fTrimArea.get(LEFT_ID);
0774: TrimArea right = (TrimArea) fTrimArea.get(RIGHT_ID);
0775:
0776: int trim_top = top.calculateTrimSize(cr.width, cr.height);
0777: int trim_bottom = bottom.calculateTrimSize(cr.width, cr.height);
0778: int trim_left = left.calculateTrimSize(cr.width, cr.height);
0779: int trim_right = right.calculateTrimSize(cr.width, cr.height);
0780:
0781: // Adjust the trim sizes to incorporate the margins for sides that
0782: // don't currently have trim
0783: if (trim_top == 0) {
0784: trim_top = marginHeight;
0785: }
0786: if (trim_bottom == 0) {
0787: trim_bottom = marginHeight;
0788: }
0789: if (trim_left == 0) {
0790: trim_left = marginWidth;
0791: }
0792: if (trim_right == 0) {
0793: trim_right = marginWidth;
0794: }
0795:
0796: Rectangle trimRect = new Rectangle(0, 0, 0, 0);
0797: switch (areaId) {
0798: case TOP:
0799: trimRect.x = cr.x;
0800: trimRect.width = cr.width;
0801: trimRect.y = cr.y;
0802: trimRect.height = trim_top;
0803: break;
0804: case BOTTOM:
0805: trimRect.x = cr.x;
0806: trimRect.width = cr.width;
0807: trimRect.y = (cr.y + cr.height) - trim_bottom;
0808: trimRect.height = trim_bottom;
0809: break;
0810: case LEFT:
0811: trimRect.x = cr.x;
0812: trimRect.width = trim_left;
0813: trimRect.y = cr.y + trim_top;
0814: trimRect.height = cr.height - (trim_top + trim_bottom);
0815: break;
0816: case RIGHT:
0817: trimRect.x = (cr.x + cr.width) - trim_right;
0818: trimRect.width = trim_right;
0819: trimRect.y = cr.y + trim_top;
0820: trimRect.height = cr.height - (trim_top + trim_bottom);
0821: break;
0822: }
0823:
0824: return trimRect;
0825: }
0826:
0827: /*
0828: * (non-Javadoc)
0829: *
0830: * @see org.eclipse.ui.internal.layout.ITrimManager#getAllTrim()
0831: */
0832: public List getAllTrim() {
0833: List trimList = new ArrayList(fTrimDescriptors.size());
0834:
0835: Iterator d = fTrimDescriptors.values().iterator();
0836: while (d.hasNext()) {
0837: TrimDescriptor desc = (TrimDescriptor) d.next();
0838: trimList.add(desc.getTrim());
0839: }
0840:
0841: return trimList;
0842: }
0843:
0844: /*
0845: * (non-Javadoc)
0846: *
0847: * @see org.eclipse.ui.internal.layout.ITrimManager#setTrimVisible(org.eclipse.ui.internal.IWindowTrim,
0848: * boolean)
0849: */
0850: public void setTrimVisible(IWindowTrim trim, boolean visible) {
0851: TrimDescriptor desc = findTrimDescription(trim.getControl());
0852:
0853: if (desc != null) {
0854: desc.setVisible(visible);
0855: }
0856: }
0857:
0858: /**
0859: * Find the trim descriptor for this control.
0860: *
0861: * @param trim
0862: * the Control to find.
0863: * @return the trim descriptor, or <code>null</code> if not found.
0864: * @since 3.2
0865: */
0866: private TrimDescriptor findTrimDescription(Control trim) {
0867: Iterator d = fTrimDescriptors.values().iterator();
0868: while (d.hasNext()) {
0869: TrimDescriptor desc = (TrimDescriptor) d.next();
0870: if (desc.getTrim().getControl() == trim) {
0871: return desc;
0872: }
0873: if (desc.getDockingCache() != null
0874: && desc.getDockingCache().getControl() == trim) {
0875: return desc;
0876: }
0877: }
0878: return null;
0879: }
0880:
0881: /**
0882: * Return the trim area associated with the given id
0883: *
0884: * @param areaId
0885: * The id of the trim area to get
0886: * @return The TrimArea or <code>null</code> if the id is not found
0887: */
0888: public TrimArea getTrimArea(int areaId) {
0889: return (TrimArea) fTrimArea.get(new Integer(areaId));
0890: }
0891:
0892: /**
0893: * Remember the persisted locations for the trim. This allows the code
0894: * to site the trim in its preferred (i.e. cached) location on creation
0895: *
0896: * @param areaId The id of the trim area being defined
0897: * @param preferredLocations A list of trim ID's
0898: */
0899: public void setPreferredLocations(int areaId,
0900: List preferredLocations) {
0901: preferredLocationMap.put(new Integer(areaId),
0902: preferredLocations);
0903: }
0904:
0905: /**
0906: * If the given id has a cached location return its preferred side
0907: *
0908: * @param trimId The id of the trim to be tested
0909: * @return The areaId of a cached id or -1 if no cache info exists
0910: */
0911: public int getPreferredArea(String trimId) {
0912: Iterator keyIter = preferredLocationMap.keySet().iterator();
0913: while (keyIter.hasNext()) {
0914: Integer key = (Integer) keyIter.next();
0915: List areaList = (List) preferredLocationMap.get(key);
0916: if (areaList.contains(trimId))
0917: return key.intValue();
0918: }
0919:
0920: return -1;
0921: }
0922:
0923: /**
0924: * If the given id has a cached location return an existing trim
0925: * element that it should be placed before (if any)
0926: *
0927: * @param trimId The id of the trim to be tested
0928: * @return The trim to be inserted before or <code>null</code>
0929: * if no cached info exists
0930: */
0931: public IWindowTrim getPreferredLocation(String trimId) {
0932: Iterator keyIter = preferredLocationMap.keySet().iterator();
0933: while (keyIter.hasNext()) {
0934: Integer key = (Integer) keyIter.next();
0935: List areaList = (List) preferredLocationMap.get(key);
0936: int index = areaList.indexOf(trimId);
0937: if (index != -1) {
0938: // OK, find the first 'real' trim after this one
0939: // This will be used as the 'beforeMe' parameter
0940: // in the 'addTrim' call
0941: for (int i = index + 1; i < areaList.size(); i++) {
0942: String id = (String) areaList.get(i);
0943: IWindowTrim trim = getTrim(id);
0944: if (trim != null)
0945: return trim;
0946: }
0947: }
0948: }
0949:
0950: return null;
0951: }
0952:
0953: /**
0954: * Disables the controls associated with visible trim elements. This
0955: * is only used during long-running WorkbenchWindow operations to prevent users
0956: * from changing the environment while the operation (i.e. a long-running editor
0957: * 'save') is in progress.
0958: *
0959: * The expected life-cycle is to first call this this method to disable any visible
0960: * trim (and caching the elements that had to be disabled) followed by a call to
0961: * 'enableTrim' passing in the list returned from this method.
0962: *
0963: * @param ignoreMe Since the current UI has a disable button in the StatusLine
0964: * we allow the caller to designate one piece of trim to ignore
0965: *
0966: * @return The list of trim controls that were disabled during this call
0967: */
0968: public List disableTrim(IWindowTrim ignoreMe) {
0969: List disabledControls = new ArrayList();
0970:
0971: // Disable all the trim -except- for 'ignoreMe'
0972: List allTrim = getAllTrim();
0973: for (Iterator trimIter = allTrim.iterator(); trimIter.hasNext();) {
0974: IWindowTrim trim = (IWindowTrim) trimIter.next();
0975: if (ignoreMe == trim)
0976: continue;
0977:
0978: Control ctrl = trim.getControl();
0979: if (ctrl == null || ctrl.isDisposed() || !ctrl.isVisible()
0980: || !ctrl.isEnabled())
0981: continue;
0982:
0983: ctrl.setEnabled(false);
0984: disabledControls.add(ctrl);
0985: }
0986:
0987: return disabledControls;
0988: }
0989:
0990: /**
0991: * Enables the controls in the list. This list is expected to be a non-modified
0992: * List as returned from a call to 'disableTrim'.
0993: * @param disabledControls The list of controls to enable
0994: */
0995: public void enableTrim(List disabledControls) {
0996: // Simply re-enable any controls in the list
0997: for (Iterator dcIter = disabledControls.iterator(); dcIter
0998: .hasNext();) {
0999: Control ctrl = (Control) dcIter.next();
1000:
1001: if (!ctrl.isDisposed() && !ctrl.isEnabled())
1002: ctrl.setEnabled(true);
1003: }
1004: }
1005: }
|