package org.lobobrowser.html.renderer; import java.awt.Graphics; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.MouseEvent; import java.util.Iterator; import org.eclipse.jdt.annotation.NonNull; import org.lobobrowser.html.domimpl.ModelNode; abstract class BaseRCollection extends BaseBoundableRenderable implements RCollection { public BaseRCollection(final RenderableContainer container, final ModelNode modelNode) { super(container, modelNode); } public void focus() { this.container.focus(); // TODO: Plus local focus } public void blur() { final RCollection parent = this.parent; if (parent != null) { parent.focus(); } else { // TODO: Remove local focus } } /** * Updates bounds of all descendent's GUI components, based on root bounds. */ public void updateWidgetBounds(final int guiX, final int guiY) { final Iterator<? extends Renderable> i = this.getRenderables(false); if (i != null) { while (i.hasNext()) { final Renderable rn = i.next(); final Renderable r = (rn instanceof PositionedRenderable) ? ((PositionedRenderable) rn).renderable : rn; if (r instanceof RCollection) { // RUIControl is a RCollection too. final RCollection rc = (RCollection) r; final Point or = rc.getOriginRelativeTo(this); // rc.updateWidgetBounds(guiX + rc.getX(), guiY + rc.getY()); rc.updateWidgetBounds(guiX + or.x, guiY + or.y); } /* int ox = guiX; int oy = guiY; Renderable r = rn; if (rn instanceof PositionedRenderable) { final PositionedRenderable pr = (PositionedRenderable) rn; final Point pro = pr.getOffset(); ox += pro.x; oy += pro.y; r = pr.renderable; } if (r instanceof RCollection) { // RUIControl is a RCollection too. final RCollection rc = (RCollection) r; final Point or = rc.getOriginRelativeTo(this); // rc.updateWidgetBounds(guiX + rc.getX(), guiY + rc.getY()); rc.updateWidgetBounds(ox + or.x, oy + or.y); } */ } } } private static boolean checkStartSelection(final Rectangle bounds, final Point selectionPoint) { if (bounds.y > selectionPoint.y) { return true; } else if ((selectionPoint.y >= bounds.y) && (selectionPoint.y < (bounds.y + bounds.height)) && (bounds.x > selectionPoint.x)) { return true; } else { return false; } } private static boolean checkEndSelection(final Rectangle bounds, final Point selectionPoint) { if (bounds.y > selectionPoint.y) { return true; } else if ((selectionPoint.y >= bounds.y) && (selectionPoint.y < (bounds.y + bounds.height)) && (selectionPoint.x < bounds.x)) { return true; } else { return false; } } public boolean paintSelection(final Graphics g, boolean inSelection, final RenderableSpot startPoint, final RenderableSpot endPoint) { // TODO: Does this work with renderables that are absolutely positioned? Point checkPoint1 = null; Point checkPoint2 = null; if (!inSelection) { final boolean isStart = startPoint.renderable == this; final boolean isEnd = endPoint.renderable == this; if (isStart && isEnd) { checkPoint1 = startPoint.getPoint(); checkPoint2 = endPoint.getPoint(); } else if (isStart) { checkPoint1 = startPoint.getPoint(); } else if (isEnd) { checkPoint1 = endPoint.getPoint(); } } else { if (startPoint.renderable == this) { checkPoint1 = startPoint.getPoint(); } else if (endPoint.renderable == this) { checkPoint1 = endPoint.getPoint(); } } final Iterator<? extends Renderable> i = this.getRenderables(true); if (i != null) { while (i.hasNext()) { final Object robj = i.next(); if (robj instanceof BoundableRenderable) { final BoundableRenderable renderable = (BoundableRenderable) robj; final Rectangle bounds = renderable.getVisualBounds(); if (!inSelection) { if ((checkPoint1 != null) && checkStartSelection(bounds, checkPoint1)) { if (checkPoint2 != null) { checkPoint1 = checkPoint2; checkPoint2 = null; } else { checkPoint1 = null; } inSelection = true; } else if ((checkPoint2 != null) && checkStartSelection(bounds, checkPoint2)) { checkPoint1 = null; checkPoint2 = null; inSelection = true; } } else if (inSelection && (checkPoint1 != null) && checkEndSelection(bounds, checkPoint1)) { return false; } final int offsetX = bounds.x; final int offsetY = bounds.y; g.translate(offsetX, offsetY); try { final boolean newInSelection = renderable.paintSelection(g, inSelection, startPoint, endPoint); if (inSelection && !newInSelection) { return false; } inSelection = newInSelection; } finally { g.translate(-offsetX, -offsetY); } } } } if (inSelection && (checkPoint1 != null)) { return false; } else if (!inSelection && ((checkPoint1 != null) || (checkPoint2 != null)) && !((checkPoint1 != null) && (checkPoint2 != null))) { // Has to have started not being in selection, // but we must start selecting without having // selected anything in the block then. return true; } return inSelection; } public boolean extractSelectionText(final StringBuffer buffer, boolean inSelection, final RenderableSpot startPoint, final RenderableSpot endPoint) { Point checkPoint1 = null; Point checkPoint2 = null; if (!inSelection) { final boolean isStart = startPoint.renderable == this; final boolean isEnd = endPoint.renderable == this; if (isStart && isEnd) { checkPoint1 = startPoint.getPoint(); checkPoint2 = endPoint.getPoint(); } else if (isStart) { checkPoint1 = startPoint.getPoint(); } else if (isEnd) { checkPoint1 = endPoint.getPoint(); } } else { if (startPoint.renderable == this) { checkPoint1 = startPoint.getPoint(); } else if (endPoint.renderable == this) { checkPoint1 = endPoint.getPoint(); } } final Iterator<? extends Renderable> i = this.getRenderables(true); if (i != null) { while (i.hasNext()) { final Renderable rn = i.next(); final Renderable robj = (rn instanceof PositionedRenderable) ? ((PositionedRenderable) rn).renderable : rn; if (robj instanceof BoundableRenderable) { final BoundableRenderable renderable = (BoundableRenderable) robj; if (!inSelection) { final Rectangle bounds = renderable.getVisualBounds(); if ((checkPoint1 != null) && checkStartSelection(bounds, checkPoint1)) { if (checkPoint2 != null) { checkPoint1 = checkPoint2; checkPoint2 = null; } else { checkPoint1 = null; } inSelection = true; } else if ((checkPoint2 != null) && checkStartSelection(bounds, checkPoint2)) { checkPoint1 = null; checkPoint2 = null; inSelection = true; } } else if (inSelection && (checkPoint1 != null) && checkEndSelection(renderable.getBounds(), checkPoint1)) { return false; } final boolean newInSelection = renderable.extractSelectionText(buffer, inSelection, startPoint, endPoint); if (inSelection && !newInSelection) { return false; } inSelection = newInSelection; } } } if (inSelection && (checkPoint1 != null)) { return false; } else if (!inSelection && ((checkPoint1 != null) || (checkPoint2 != null)) && !((checkPoint1 != null) && (checkPoint2 != null))) { // Has to have started not being in selection, // but we must start selecting without having // selected anything in the block then. return true; } return inSelection; } public void invalidateLayoutDeep() { // TODO: May be pretty inefficient in RLine's // if it's true that non-layable components // are not in RLine's anymore. this.invalidateLayoutLocal(); final Iterator<? extends Renderable> renderables = this.getRenderables(true); if (renderables != null) { while (renderables.hasNext()) { final Renderable rn = renderables.next(); final Renderable r = (rn instanceof PositionedRenderable) ? ((PositionedRenderable) rn).renderable : rn; if (r instanceof RCollection) { ((RCollection) r).invalidateLayoutDeep(); } } } } private BoundableRenderable renderableWithMouse = null; @Override public void onMouseMoved(final MouseEvent event, final int x, final int y, final boolean triggerEvent, final ModelNode limit) { super.onMouseMoved(event, x, y, triggerEvent, limit); final BoundableRenderable oldRenderable = this.renderableWithMouse; final Renderable r = this.getRenderable(x, y); final BoundableRenderable newRenderable = r instanceof BoundableRenderable ? (BoundableRenderable) r : null; ModelNode newLimit; if (this.isContainedByNode()) { newLimit = this.modelNode; } else { newLimit = limit; } final boolean changed = oldRenderable != newRenderable; if (changed) { if (oldRenderable != null) { oldRenderable.onMouseOut(event, x - oldRenderable.getVisualX(), y - oldRenderable.getVisualY(), newLimit); } this.renderableWithMouse = newRenderable; } // Must recurse always if (newRenderable != null) { // newRenderable.onMouseMoved(event, x - newRenderable.getVisualX(), y - newRenderable.getVisualY(), changed, newLimit); final Point or = newRenderable.getOriginRelativeTo(this); newRenderable.onMouseMoved(event, x - or.x, y - or.y, changed, newLimit); } } @Override public boolean onMousePressed(MouseEvent event, int x, int y) { final Renderable r = this.getRenderable(x, y); final RCollection newRenderable = r instanceof RCollection ? (RCollection) r : null; if (newRenderable != null) { final Point or = newRenderable.getOriginRelativeTo(this); if(!newRenderable.onMousePressed(event, x - or.x, y - or.y)) { return false; } } if (!HtmlController.getInstance().onMouseDown(this.modelNode, event, x, y)) { return false; } return true; } @Override public boolean onMouseClick(MouseEvent event, int x, int y) { final Renderable r = this.getRenderable(x, y); final RCollection newRenderable = r instanceof RCollection ? (RCollection) r : null; if (newRenderable != null) { final Point or = newRenderable.getOriginRelativeTo(this); if(!newRenderable.onMouseClick(event, x - or.x, y - or.y)) { return false; } } if (!HtmlController.getInstance().onMouseClick(this.modelNode, event, x, y)) { return false; } return true; } @Override public void onMouseOut(final MouseEvent event, final int x, final int y, final ModelNode limit) { super.onMouseOut(event, x, y, limit); final BoundableRenderable oldRenderable = this.renderableWithMouse; if (oldRenderable != null) { this.renderableWithMouse = null; ModelNode newLimit; if (this.isContainedByNode()) { newLimit = this.modelNode; } else { newLimit = limit; } final Point or = oldRenderable.getOriginRelativeTo(this); oldRenderable.onMouseOut(event, x - or.x, y - or.y, newLimit); } } public BoundableRenderable getRenderable(final int x, final int y) { final Iterator<? extends Renderable> i = this.getRenderables(true); if (i != null) { while (i.hasNext()) { final Renderable rn = i.next(); final Renderable r = rn; if (r instanceof BoundableRenderable) { final BoundableRenderable br = (BoundableRenderable) r; /* if (br instanceof RBlockViewport) { return br; }*/ if ((!br.isDelegated()) && br.contains(x, y)) { return br; } } else if (r instanceof PositionedRenderable) { PositionedRenderable pr = (PositionedRenderable) r; if (pr.contains(x, y)) { return pr.renderable; } } } } return null; } public boolean onMiddleClick(final MouseEvent event, final int x, final int y) { final BoundableRenderable br = this.getRenderable(x, y); if (br == null) { return HtmlController.getInstance().onMiddleClick(this.modelNode, event, x, y); } else { final Point or = br.getOriginRelativeTo(this); return br.onMiddleClick(event, x - or.x, y - or.y); } } public boolean onRightClick(final MouseEvent event, final int x, final int y) { final BoundableRenderable br = this.getRenderable(x, y); if (br == null) { return HtmlController.getInstance().onContextMenu(this.modelNode, event, x, y); } else { final Point or = br.getOriginRelativeTo(this); return br.onRightClick(event, x - or.x, y - or.y); } } @Override public Rectangle getClipBoundsWithoutInsets() { // TODO return getClipBounds(); } @Override public boolean isReadyToPaint() { final Iterator<@NonNull ? extends Renderable> renderables = getRenderables(); if (renderables == null) { return true; } while (renderables.hasNext()) { final Renderable next = renderables.next(); if (!next.isReadyToPaint()) { return false; } } return true; } }