| /******************************************************************************* |
| * 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(); |
| |
| ICSSStyle _style; |
| |
| // NOTE: here keep the element is only for debug use. CSSFigure shouldn't |
| // require an element. |
| // Element _element; |
| |
| // if this field is set, then regetLayout() will still return this layout, |
| // without going through the CSS resolution |
| CSSLayout _fixedLayout; |
| |
| public CSSFigure() { |
| _style = DefaultStyle.getInstance(); |
| invalidateCSS(); |
| } |
| |
| public CSSFigure(ICSSStyle style) { |
| _style = style; |
| // _element = element; |
| invalidateCSS(); |
| } |
| |
| public ICSSStyle getCSSStyle() { |
| return _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); |
| } |
| |
| 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); |
| } |
| |
| 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 |
| */ |
| 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._absoluteContext != null) { |
| BlockBox blockbox = csslayout._absoluteContext._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 Border} or {@link 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); |
| |
| } |
| |
| } |