/******************************************************************************* * 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.Image; 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(); //handle background-image Object bgImage = getCSSStyle().getStyleProperty(ICSSPropertyID.ATTR_BACKGROUND_IMAGE); if (bgImage instanceof Image) { g.setClip(bounds); g.drawImage((Image)bgImage, bounds.x, bounds.y); 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); } }