blob: a6c5289e0fb9d6d313ec55aa12f86516219d551e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006 Sybase, Inc. 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:
* Sybase, Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.jst.pagedesigner.css2.layout;
import java.util.List;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.LayoutManager;
import org.eclipse.draw2d.geometry.Insets;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.jst.jsf.common.ui.internal.logging.Logger;
import org.eclipse.jst.pagedesigner.PDPlugin;
import org.eclipse.jst.pagedesigner.css2.ICSSStyle;
import org.eclipse.jst.pagedesigner.css2.border.CSSBorder;
import org.eclipse.jst.pagedesigner.css2.property.ICSSPropertyID;
import org.eclipse.jst.pagedesigner.css2.property.VisibilityMeta;
import org.eclipse.jst.pagedesigner.css2.style.DefaultStyle;
import org.eclipse.jst.pagedesigner.css2.style.ITagEditInfo;
import org.eclipse.jst.pagedesigner.css2.widget.BorderUtil;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
/**
* Normally a CSSFigure is a container. It's layout will be driven by different
* display type information from the style.
*
* Each CSSFigure will be driven by ICSSStyle, the display type of the ICSSStyle
* will decide the layout to be used for the figure.
*
* @author mengbo
*/
public class CSSFigure extends FlowFigure implements ICSSFigure {
private static Logger _log = PDPlugin.getLogger(CSSFigure.class);
private static final Rectangle PRIVATE_RECT = new Rectangle();
private ICSSStyle _style;
// if this field is set, then regetLayout() will still return this layout,
// without going through the CSS resolution
private CSSLayout _fixedLayout;
/**
* Default constructor
* Equivalent to CSSFigure(DefaultStyle.getInstance())
*/
public CSSFigure() {
this(DefaultStyle.getInstance());
}
/**
* @param style
*/
public CSSFigure(ICSSStyle style) {
_style = style;
invalidateCSS();
}
public ICSSStyle getCSSStyle() {
return _style;
}
/**
* @param style
*/
public void setCSSStyle(ICSSStyle style) {
_style = style;
invalidateCSS();
}
public void revalidate() {
CSSLayout layout = (CSSLayout) getLayoutManager();
layout.figureRevalidate();
super.revalidate();
}
/**
* this method is called when the css source noticed style change. So tell
* the figure should invalidate its cached data.
*/
public void invalidateCSS() {
// maybe we changed from inline to block or block to inline
// XXX: or even to table?
CSSLayout layout = regetLayout(getLayoutManager());
this.setLayoutManager(layout);
}
/**
* @param layout
*/
public void setFixedLayoutManager(CSSLayout layout) {
this._fixedLayout = layout;
this.setLayoutManager(regetLayout(getLayoutManager()));
}
/*
* (non-Javadoc)
*
* @see org.eclipse.draw2d.Figure#setLayoutManager(org.eclipse.draw2d.LayoutManager)
*/
public void setLayoutManager(LayoutManager manager) {
LayoutManager old = getLayoutManager();
if (old != manager) {
FlowContext context = null;
if (old instanceof FlowFigureLayout) {
context = ((FlowFigureLayout) old).getOriginalFlowContext();
}
if (manager instanceof FlowFigureLayout) {
((FlowFigureLayout) manager).setOriginalFlowContext(context);
}
if (manager instanceof FlowContext) {
List list = getChildren();
for (int i = 0, size = list.size(); i < size; i++) {
try {
((FlowFigure) list.get(i))
.setOriginalFlowContext((FlowContext) manager);
} catch (ClassCastException classcastexception) {
// Error in flowContext setting.
_log.error("Error.CSSFigure.0", classcastexception); //$NON-NLS-1$
}
}
}
}
super.setLayoutManager(manager);
}
/**
* @param old
* @return the layout
*/
protected CSSLayout regetLayout(LayoutManager old) {
if (_fixedLayout != null) {
return _fixedLayout;
}
CSSLayout layout = DisplayToLayout.displayToLayout(this, getCSSStyle()
.getDisplay(), old);
if (layout != null) {
return layout;
}
return new CSSInlineFlowLayout(this);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.draw2d.Figure#containsPoint(int, int)
*/
public boolean containsPoint(int x, int y) {
// check whether any float figure contains it.
// FIXME: need check floating figure here!!!
if (!super.containsPoint(x, y)) {
return false;
}
List frags = getFragmentsForRead();
// Here we should not get void pointer.
if (frags != null) {
for (int i = 0; i < frags.size(); i++) {
FlowBox box = (FlowBox) frags.get(i);
if (box != null && box.containsPoint(x, y)) {
return true;
}
}
}
return false;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jst.pagedesigner.css2.layout.ICSSFigure#getFragmentsForRead()
*/
public List getFragmentsForRead() {
CSSLayout layout = (CSSLayout) getLayoutManager();
return layout.getFragmentsForRead();
}
/**
* this method is a shortcut to getFragmentsForRead
*
* @return fragment bounds
*/
public Rectangle[] getFragmentsBounds() {
List list = getFragmentsForRead();
if (list == null || list.size() == 0) {
// should not happen. but still handle it.
return new Rectangle[] { getBounds() };
}
Rectangle[] ret = new Rectangle[list.size()];
for (int i = 0, size = list.size(); i < size; i++) {
FlowBox box = (FlowBox) list.get(i);
ret[i] = new Rectangle(box._x, box._y, box.getWidth(), box
.getHeight());
}
return ret;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.draw2d.IFigure#setBounds(org.eclipse.draw2d.geometry.Rectangle)
*/
public void setBounds(Rectangle r) {
if (getBounds().equals(r)) {
return;
}
boolean invalidate = getBounds().width != r.width
|| getBounds().height != r.height;
super.setBounds(r);
CSSLayout layout = (CSSLayout) this.getLayoutManager();
layout.setBoundsCalled(r, invalidate);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigure#postValidate()
*/
public void postValidate() {
CSSLayout layout = (CSSLayout) getLayoutManager();
layout.postValidateForAbsolute();
layout.postValidate();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.draw2d.IFigure#validate()
*/
public void validate() {
super.validate();
// should not call this.postValidate() here. PostValidate() should
// only be started from the FlowPage. Otherwise it will be called
// multiple times on a figure.
// this.postValidate();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.draw2d.Figure#useLocalCoordinates()
*/
protected boolean useLocalCoordinates() {
CSSLayout layout = (CSSLayout) getLayoutManager();
if (layout == null) {
return false;
}
return layout.useLocalCoordinates();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.draw2d.IFigure#paint(org.eclipse.draw2d.Graphics)
*/
public void paint(Graphics graphics) {
ICSSStyle style = getCSSStyle();
if (style != null) {
Object visibility = style
.getStyleProperty(ICSSPropertyID.ATTR_VISIBILITY);
// handle visibility: hidden here.
// TODO: "collapse" is not supported yet!
if (VisibilityMeta.HIDDEN.equals(visibility)) {
return;
}
}
CSSLayout layout = (CSSLayout) this.getLayoutManager();
graphics.pushState();
try {
paintFigure(graphics);
graphics.restoreState();
paintClientArea(graphics);
if (layout instanceof ICSSPainter2) {
if (useLocalCoordinates()) {
graphics.translate(getBounds().x + getInsets().left,
getBounds().y + getInsets().top);
((ICSSPainter2) layout).paintFigurePostClientArea(graphics);
graphics.restoreState();
} else {
((ICSSPainter2) layout).paintFigurePostClientArea(graphics);
}
}
paintBorder(graphics);
} finally {
graphics.popState();
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigure#paintFigure(org.eclipse.draw2d.Graphics)
*/
protected void paintFigure(Graphics g) {
Color rgbColor = null;
boolean fillArea = false;
Object bg = getCSSStyle().getBackgroundColor();
if (bg instanceof RGB) {
rgbColor = new Color(null, (RGB) bg);
g.setBackgroundColor(rgbColor);
fillArea = true;
} else if (bg instanceof Color) {
g.setBackgroundColor((Color) bg);
fillArea = true;
}
if (fillArea) {
List fragments = getFragmentsForRead();
for (int i = 0, n = fragments.size(); i < n; i++) {
Object obj = fragments.get(i);
if (obj instanceof FlowBox) {
FlowBox box = (FlowBox) obj;
g.fillRectangle(box._x, box._y, box.getWidth(), box
.getHeight());
}
}
}
if (rgbColor != null) {
rgbColor.dispose();
}
g.restoreState();
LayoutManager layout = getLayoutManager();
if (layout instanceof ICSSPainter) {
if (useLocalCoordinates()) {
g.translate(getBounds().x + getInsets().left, getBounds().y
+ getInsets().top);
((ICSSPainter) layout).paintFigure(g);
g.restoreState();
} else {
((ICSSPainter) layout).paintFigure(g);
}
}
// paint selected mode here.
paintSelection(g);
if (Debug.DEBUG_BOX) {
// draw two levels of boxes. Since normally each figure will only
// have two levels of boxes.
List fragments = this.getFragmentsForRead();
for (int i = 0, size = fragments.size(); i < size; i++) {
FlowBox box = (FlowBox) fragments.get(i);
BoxUtil.drawBox(g, box);
if (box instanceof BlockBox) {
BlockBox compositeBox = (BlockBox) box;
List list = compositeBox.getFragments();
for (int j = 0; j < list.size(); j++) {
g.translate(this.getInsets().left,
this.getInsets().right);
BoxUtil.drawBox(g, (FlowBox) list.get(j));
g.restoreState();
}
}
}
}
if (Debug.DEBUG_BASELINE) {
List fragments = this.getFragmentsForRead();
for (int i = 0, size = fragments.size(); i < size; i++) {
Object obj = fragments.get(i);
if (obj instanceof LineBox) {
LineBox linebox = (LineBox) obj;
g.setForegroundColor(ColorConstants.red);
g.drawLine(linebox._x, linebox._y + linebox.getAscent(),
linebox._x + linebox.getWidth(), linebox._y
+ linebox.getAscent());
}
}
}
if (Debug.DEBUG_BORDERPADDING) {
if (this.getLayoutManager() instanceof CSSBlockFlowLayout) {
g.setLineWidth(1);
Rectangle rect = getBounds().getCopy().crop(getInsets());
g.setForegroundColor(ColorConstants.green);
g.drawRectangle(rect);
g.setForegroundColor(ColorConstants.red);
g.drawRectangle(getBounds());
}
}
if (Debug.DEBUG_BOX) {
CSSLayout csslayout = (CSSLayout) this.getLayoutManager();
if (csslayout.getAbsoluteContext() != null) {
BlockBox blockbox = csslayout.getAbsoluteContext()._blockBox;
g.setLineWidth(1);
g.setForegroundColor(ColorConstants.green);
g.drawRectangle(blockbox._x, blockbox._y, blockbox.getWidth(),
blockbox.getHeight());
}
}
}
/**
* Paints this Figure's client area. The client area is typically defined as
* the anything inside the Figure's {@link org.eclipse.draw2d.Border} or {@link org.eclipse.draw2d.geometry.Insets}, and
* by default includes the children of this Figure. On return, this method
* must leave the given Graphics in its initial state.
*
* @param graphics
* The Graphics used to paint
* @since 2.0
*/
protected void paintClientArea(Graphics graphics) {
if (this.getChildren().isEmpty()) {
return;
}
Object overflow = ICSSPropertyID.VAL_VISIBLE;
ICSSStyle style = this.getCSSStyle();
if (style != null) {
overflow = style.getStyleProperty(ICSSPropertyID.ATTR_OVERFLOW);
}
boolean optimizeClip = ICSSPropertyID.VAL_VISIBLE.equals(overflow);
if (useLocalCoordinates()) {
graphics.translate(getBounds().x + getInsets().left, getBounds().y
+ getInsets().top);
if (!optimizeClip) {
graphics.clipRect(getClientArea(PRIVATE_RECT));
}
graphics.pushState();
paintChildren(graphics);
graphics.popState();
graphics.restoreState();
} else {
if (optimizeClip) {
paintChildren(graphics);
} else {
graphics.clipRect(getClientArea(PRIVATE_RECT));
graphics.pushState();
paintChildren(graphics);
graphics.popState();
graphics.restoreState();
}
}
}
/**
* @param g
*/
protected void paintSelection(Graphics g) {
ICSSStyle style = this.getCSSStyle();
if (style != null) {
if (style.isInSelection()) {
ITagEditInfo editInfo = (ITagEditInfo) style
.getAdapter(ITagEditInfo.class);
if (editInfo != null && editInfo.isWidget()) {
BorderUtil.maskFigure(this, g);
}
}
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.draw2d.Figure#paintBorder(org.eclipse.draw2d.Graphics)
*/
protected void paintBorder(Graphics graphics) {
CSSLayout layout = (CSSLayout) getLayoutManager();
if (layout != null && !layout.handlingBorderForBlock()) {
return;
}
ICSSStyle style = this.getCSSStyle();
if (style != null) {
CSSBorder border1 = new CSSBorder(this.getCSSStyle());
border1.paint(this, graphics, NO_INSETS);
// draw a border for those special elements like <h:form>, etc.
ITagEditInfo editInfo = (ITagEditInfo) style
.getAdapter(ITagEditInfo.class);
if (editInfo != null && editInfo.needBorderDecorator()) {
BorderUtil.drawBorderDecorator(this, graphics);
}
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.draw2d.IFigure#getInsets()
*/
public Insets getInsets() {
CSSLayout layout = (CSSLayout) getLayoutManager();
if (layout != null && !layout.handlingBorderForBlock()) {
return new Insets();
}
ICSSStyle style = this.getCSSStyle();
if (style != null) {
return style.getBorderInsets().getAdded(style.getPaddingInsets());
}
return new Insets();
}
/**
* FIXME: need trace the implementation of Figure.invalidate() We want to
* just mark this figure as invalid, but don't want to the layout get
* invalidated.
*
*/
public void invalidate2() {
if (!isValid())
return;
// if (getLayoutManager() != null)
// getLayoutManager().invalidate();
setValid(false);
}
}