/******************************************************************************* * 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.viewer; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.eclipse.core.runtime.Assert; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.gef.EditPart; import org.eclipse.jst.pagedesigner.validation.caret.ActionData; import org.eclipse.jst.pagedesigner.validation.caret.IPositionMediator; /** * FlowBoxLine collects EditParts in a line that cover the x or y coordinate of * design view caret. An EditPart may be a widget that can't contains children, * a TextEditPart which contains a set of flowbox, or a widgets container which * contains some other editparts. For container, there are two types: white box * to visitor, that is the container * visitor should consider its content, like * <A>, <B>. etc, or black box to visitor, like <TABLE>. Black box means the * container will be considered as a whole from outside. For non-container * widget we only see TextEditPart can be broken at line end. For black box, * only start box or latest box are used for final reference, for white box, we * will process its content for reference <@see * EditPartPositionHelper.findEditPartPosition>. For Text, the char that is * closest to caret will be referenced. In this line class, tree types of * EditPart are collected: TextEditPart, Widget, BlackBox container. * * @author mengbo */ public class FlowBoxLine { private int _x; private int _y; private int _height; private int _width; private HashMap _parts = new HashMap(); private IPositionMediator _validator; private Point _point; /** * @param rect * @param validator * @param point */ public FlowBoxLine(Rectangle rect, IPositionMediator validator, Point point) { _x = rect.x; _y = rect.y; _width = rect.width; _height = rect.height; _validator = validator; _point = point; } /** * @return Returns the _height. */ public int getHeight() { return _height; } /** * @return Returns the _width. */ public int getWidth() { return _width; } /** * @return Returns the _x. */ public int getX() { return _x; } /** * @return Returns the _y. */ public int getY() { return _y; } /** * @return the part list */ public Map getPartsList() { return _parts; } /** * @return the right bottom coordiate */ public Point getRightBottom() { return new Point(_x + _width, _y + _height); } /** * @param part * @param point * @return layout part added */ public boolean addLayoutPart(EditPart part, Point point) { Assert.isTrue(part != null && point != null); Rectangle rect = null; LayoutPart lPart = new LayoutPart(part, point); if (_parts.size() == 0) { resetBounds(lPart); return true; } if (!interact(lPart)) { if (closer(lPart)) { resetBounds(lPart); return true; } return false; } rect = lPart.getAbsoluteBounds(); int xx = Math.min(rect.x, _x); int width = Math.max(rect.getRight().x, getRightBottom().x) - xx; int yy = Math.min(rect.y, _y); int height = Math.max(rect.getBottom().y, getRightBottom().y) - yy; _x = xx; _y = yy; _width = width; _height = height; _parts.put(part, lPart); return true; } /** * @param lPart * @return true if layout part is within the right bottom corner of the line */ public boolean interact(LayoutPart lPart) { Rectangle rect = lPart.getAbsoluteBounds(); return !(rect.getBottom().y <= _y || getRightBottom().y <= rect.y); } /** * @param part * @return true if the line contains part */ public boolean contains(EditPart part) { return _parts.containsKey(part); } /** * @param part * @return true if the line contains part */ public boolean contains(LayoutPart part) { return _parts.containsValue(part); } /** * @param part * @return the layout part for part */ public LayoutPart getLayoutPart(EditPart part) { return (LayoutPart) _parts.get(part); } // /** * For vertical movement, we need to see if there is part cover p.x. * * @return the closest edit part */ public EditPart getClosestPart() { if (_parts.isEmpty()) { return null; } Collection parts = _parts.values(); Iterator iterator = parts.iterator(); LayoutPart closestPart = (LayoutPart) iterator.next(); if (iterator.hasNext()) { while (iterator.hasNext()) { LayoutPart nextPart = (LayoutPart) iterator.next(); closestPart = CaretPositionResolver.getCloserPart(closestPart, nextPart, _point); } } // for children. LayoutPart result = null; if (_validator.getActionData().getActionType() == ActionData.KEYBOARD_NAVAGATION || // closestPart.isInline()) { result = CaretPositionResolver.getInstance(_validator, _point) .resolveClosestPartFrom(closestPart); } else { result = closestPart; } if (result != null) { return result.getPart(); } return null; } /** * See how close the part,box is closer to point, if it is closer than * current FlowBoxLine. return true; */ private boolean closer(LayoutPart lPart) { int lineYOffset = Math.abs(CaretPositionResolver.getYDistance( getBounds(), _point)); int partYOffset = Math.abs(CaretPositionResolver.getYDistance(lPart .getAbsoluteBounds(), _point)); return lineYOffset > partYOffset; } /** * @return the bounding rectangle of the line */ public Rectangle getBounds() { return new Rectangle(_x, _y, _width, _height); } private void resetBounds(Rectangle rect) { _x = rect.x; _y = rect.y; _width = rect.width; _height = rect.height; } private void resetBounds(LayoutPart lPart) { EditPart part = lPart.getPart(); Rectangle rect = lPart.getAbsoluteBounds(); resetBounds(rect); _parts.clear(); _parts.put(part, lPart); } }