blob: 19cce1e5aa55efcbbcdeb8f5a9a17919b6ecc364 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2015 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
* Christian Walther (Indel AG) - Bug 399458: Fix layout overlap in line-wrapped trim bar
* Christian Walther (Indel AG) - Bug 389012: Fix division by zero in TrimBarLayout
* Marc-Andre Laperle (Ericsson) - Bug 466233: Toolbar items are wrongly rendered into a "drop-down"
*******************************************************************************/
package org.eclipse.e4.ui.workbench.renderers.swt;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.e4.ui.internal.workbench.swt.AbstractPartRenderer;
import org.eclipse.e4.ui.model.application.ui.MUIElement;
import org.eclipse.e4.ui.model.application.ui.basic.MTrimBar;
import org.eclipse.e4.ui.model.application.ui.basic.MTrimElement;
import org.eclipse.e4.ui.model.application.ui.menu.MToolBar;
import org.eclipse.swt.SWT;
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.swt.widgets.Layout;
import org.eclipse.swt.widgets.ToolBar;
public class TrimBarLayout extends Layout {
class TrimLine {
Map<Control, Point> sizeMap = new HashMap<Control, Point>();
List<Control> ctrls = new ArrayList<Control>();
int spacerCount = 0;
int extraSpace = 0;
int major = 0;
int minor = 0;
public void addControl(Control ctrl) {
Point ctrlSize = computeSize(ctrl);
int ctrlMajor = horizontal ? ctrlSize.x : ctrlSize.y;
int ctrlMinor = horizontal ? ctrlSize.y : ctrlSize.x;
major += ctrlMajor;
if (ctrlMinor > minor)
minor = ctrlMinor;
sizeMap.put(ctrl, ctrlSize);
ctrls.add(ctrl);
if (isSpacer(ctrl))
spacerCount++;
}
public void mergeSegment(TrimLine segment) {
sizeMap.putAll(segment.sizeMap);
ctrls.addAll(segment.ctrls);
major += segment.major;
if (segment.minor > minor)
minor = segment.minor;
spacerCount += segment.spacerCount;
}
}
private List<TrimLine> lines = new ArrayList<TrimLine>();
public static String SPACER = "stretch"; //$NON-NLS-1$
public static String GLUE = "glue"; //$NON-NLS-1$
private boolean horizontal;
public int marginLeft = 0;
public int marginRight = 0;
public int marginTop = 0;
public int marginBottom = 0;
public int wrapSpacing = 0;
public TrimBarLayout(boolean horizontal) {
this.horizontal = horizontal;
}
@Override
protected Point computeSize(Composite composite, int wHint, int hHint,
boolean flushCache) {
if (flushCache) {
// Clear the current cache
lines.clear();
}
// First, hide any empty toolbars
MTrimBar bar = (MTrimBar) composite
.getData(AbstractPartRenderer.OWNING_ME);
for (MTrimElement te : bar.getChildren()) {
hideManagedTB(te);
}
int totalMajor = horizontal ? wHint - (marginLeft + marginRight)
: hHint - (marginTop + marginBottom);
int totalMinor = 0;
int spaceLeft = totalMajor;
TrimLine curLine = new TrimLine();
Control[] kids = composite.getChildren();
for (int i = 0; i < kids.length; i++) {
Control ctrl = kids[i];
// GLUE Handling; gather any glued controls up into a 'segment'
TrimLine segment = new TrimLine();
segment.addControl(ctrl);
while (i < (kids.length - 2) && isGlue(kids[i + 1])) {
segment.addControl(kids[i + 1]);
segment.addControl(kids[i + 2]);
i += 2;
}
// Do we have enough space ?
if (segment.major <= spaceLeft) {
// Yes, add the segment to the current line
curLine.mergeSegment(segment);
spaceLeft -= segment.major;
} else {
// No, cache the current line and start a new one
curLine.extraSpace = spaceLeft;
lines.add(curLine);
totalMinor += curLine.minor;
curLine = segment;
spaceLeft = totalMajor - segment.major;
}
}
if (curLine.ctrls.size() > 0) {
curLine.extraSpace = spaceLeft;
lines.add(curLine);
totalMinor += curLine.minor;
}
// Adjust the 'totalMinor' to account for the margins
int totalWrapSpacing = (lines.size() - 1) * wrapSpacing;
totalMinor += horizontal ? (marginTop + marginBottom)
+ totalWrapSpacing : (marginLeft + marginRight)
+ totalWrapSpacing;
Point calcSize = horizontal ? new Point(wHint, totalMinor) : new Point(
totalMinor, hHint);
return calcSize;
}
private Point computeSize(Control ctrl) {
Point ctrlSize = ctrl.computeSize(SWT.DEFAULT, SWT.DEFAULT);
// Hack! the StatusLine doesn't compute a useable size
if (isStatusLine(ctrl)) {
ctrlSize.x = 375;
ctrlSize.y = 26;
}
return ctrlSize;
}
/**
* This is a HACK ! Due to compatibility restrictions we have the case where
* we <b>must</b> leave 'empty' toolbars in the trim. This code detects this
* particular scenario and hides any TB's of this type...
*
* @param te
* The proposed trim element
* @return <code>true</code> iff this element represents an empty managed
* TB.
*/
private boolean hideManagedTB(MTrimElement te) {
if (!(te instanceof MToolBar)
|| !(te.getRenderer() instanceof ToolBarManagerRenderer))
return false;
if (!(te.getWidget() instanceof Composite))
return false;
Composite teComp = (Composite) te.getWidget();
Control[] kids = teComp.getChildren();
if (kids.length != 1 || !(kids[0] instanceof ToolBar))
return false;
boolean barVisible = ((ToolBar) kids[0]).getItemCount() > 0;
// HACK! The trim dragging code uses the visible attribute as well
// this is a local 'lock' to prevent the layout from messing with it
if (!te.getTags().contains("LockVisibility")) { //$NON-NLS-1$
te.setVisible(barVisible);
}
return !barVisible;
}
@Override
protected void layout(Composite composite, boolean flushCache) {
if (flushCache) {
// Clear the current cache
lines.clear();
}
Rectangle bounds = composite.getBounds();
// offset the rectangle to allow for the margins
bounds.x = marginLeft;
bounds.y = marginTop;
bounds.width -= (marginLeft + marginRight);
bounds.height -= (marginTop + marginBottom);
// If we were called directly we need to fill the caches
if (lines.size() == 0) {
if (horizontal)
computeSize(composite, bounds.width, SWT.DEFAULT, true);
else
computeSize(composite, SWT.DEFAULT, bounds.height, true);
}
if (lines.size() == 0)
return;
for (TrimLine curLine : lines) {
tileLine(curLine, bounds);
if (horizontal)
bounds.y += curLine.minor + wrapSpacing;
else
bounds.x += curLine.minor + wrapSpacing;
}
}
/**
* @param curLine
* @param bounds
*/
private void tileLine(TrimLine curLine, Rectangle bounds) {
int curX = bounds.x;
int curY = bounds.y;
int remainingExtraSpace = curLine.extraSpace;
int remainingSpacerCount = curLine.spacerCount;
for (Control ctrl : curLine.ctrls) {
if (ctrl.isDisposed()) {
continue;
}
Point ctrlSize = curLine.sizeMap.get(ctrl);
int ctrlWidth = ctrlSize.x;
int ctrlHeight = ctrlSize.y;
boolean zeroSize = ctrlWidth == 0 && ctrlHeight == 0;
// If its a 'spacer' then add any available 'extra' space to it
if (isSpacer(ctrl)) {
int extra = remainingExtraSpace / remainingSpacerCount;
if (horizontal) {
ctrlWidth += extra;
// leave out 4 pixels at the bottom to avoid overlapping the
// 1px bottom border of the toolbar (bug 389941)
ctrl.setBounds(curX, curY, ctrlWidth, curLine.minor - 4);
} else {
ctrlHeight += extra;
ctrl.setBounds(curX, curY, curLine.minor, ctrlHeight);
}
zeroSize = false;
remainingExtraSpace -= extra;
remainingSpacerCount--;
}
if (horizontal) {
int offset = (curLine.minor - ctrlHeight) / 2;
if (!isSpacer(ctrl)) {
if (!zeroSize)
ctrl.setBounds(curX, curY + offset, ctrlWidth,
ctrlHeight);
else
ctrl.setBounds(curX, curY, 0, 0);
}
curX += ctrlWidth;
} else {
int offset = (curLine.minor - ctrlWidth) / 2;
ctrl.setBounds(curX + offset, curY, ctrlWidth, ctrlHeight);
curY += ctrlHeight;
}
}
}
private boolean isSpacer(Control ctrl) {
MUIElement element = (MUIElement) ctrl
.getData(AbstractPartRenderer.OWNING_ME);
if (element != null && element.getTags().contains(SPACER))
return true;
return false;
}
private boolean isGlue(Control ctrl) {
MUIElement element = (MUIElement) ctrl
.getData(AbstractPartRenderer.OWNING_ME);
if (element != null && element.getTags().contains(GLUE))
return true;
return false;
}
private boolean isStatusLine(Control ctrl) {
MUIElement element = (MUIElement) ctrl
.getData(AbstractPartRenderer.OWNING_ME);
if (element != null && element.getElementId() != null
&& element.getElementId().equals("org.eclipse.ui.StatusLine")) //$NON-NLS-1$
return true;
return false;
}
/**
* @param trimPos
* @return
*/
public Control ctrlFromPoint(Composite trimComp, Point trimPos) {
if (trimComp == null || trimComp.isDisposed() || lines == null
|| lines.size() == 0)
return null;
Control[] kids = trimComp.getChildren();
for (int i = 0; i < kids.length; i++) {
if (kids[i].isDisposed())
continue;
if (kids[i].getBounds().contains(trimPos))
return kids[i];
}
return null;
}
}