/*
GNU GENERAL LICENSE
Copyright (C) 2006 The Lobo Project. Copyright (C) 2014 - 2017 Lobo Evolution
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
verion 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General License for more details.
You should have received a copy of the GNU General Public
along with this program. If not, see <http://www.gnu.org/licenses/>.
Contact info: lobochief@users.sourceforge.net; ivan.difrancesco@yahoo.it
*/
/*
* Created on Nov 19, 2005
*/
package org.lobobrowser.html.renderer;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import org.lobobrowser.html.HtmlRendererContext;
import org.lobobrowser.html.control.RUIControl;
import org.lobobrowser.html.dombl.ModelNode;
import org.lobobrowser.html.domimpl.HTMLElementImpl;
import org.lobobrowser.html.layout.LayoutKey;
import org.lobobrowser.html.layout.LayoutValue;
import org.lobobrowser.html.renderstate.RenderState;
import org.lobobrowser.html.style.RenderThreadState;
import org.lobobrowser.http.UserAgentContext;
import org.lobobrowser.util.Objects;
/**
* The Class RTable.
*/
public class RTable extends BaseElementRenderable {
/** The Constant MAX_CACHE_SIZE. */
private static final int MAX_CACHE_SIZE = 10;
/** The cached layout. */
private final Map<LayoutKey, LayoutValue> cachedLayout = new HashMap<LayoutKey, LayoutValue>(5);
/** The table matrix. */
private final TableMatrix tableMatrix;
/** The positioned renderables. */
private SortedSet<PositionedRenderable> positionedRenderables;
/** The other ordinal. */
private int otherOrdinal;
/** The last layout key. */
private LayoutKey lastLayoutKey = null;
/** The last layout value. */
private LayoutValue lastLayoutValue = null;
/**
* Instantiates a new r table.
*
* @param modelNode
* the model node
* @param pcontext
* the pcontext
* @param rcontext
* the rcontext
* @param frameContext
* the frame context
* @param container
* the container
*/
public RTable(HTMLElementImpl modelNode, UserAgentContext pcontext, HtmlRendererContext rcontext,
FrameContext frameContext, RenderableContainer container) {
super(container, modelNode, pcontext);
this.tableMatrix = new TableMatrix(modelNode, pcontext, rcontext, frameContext, this, this);
}
/*
* (non-Javadoc)
*
* @see org.lobobrowser.html.renderer.RElement#getVAlign()
*/
@Override
public int getVAlign() {
// Not used
return VALIGN_BASELINE;
}
/*
* (non-Javadoc)
*
* @see
* org.lobobrowser.html.renderer.BaseElementRenderable#getWidthElement()
*/
@Override
protected int getWidthElement() {
return this.width;
}
/*
* (non-Javadoc)
*
* @see
* org.lobobrowser.html.renderer.BaseElementRenderable#getHeightElement()
*/
@Override
protected int getHeightElement() {
return this.tableMatrix.getTableHeightWithoutCaption();
}
/*
* (non-Javadoc)
*
* @see org.lobobrowser.html.renderer.BaseElementRenderable#getStartY()
*/
@Override
protected int getStartY() {
return this.tableMatrix.getStartYWithoutCaption();
}
/*
* (non-Javadoc)
*
* @see org.lobobrowser.html.renderer.BaseElementRenderable#paint(java.awt.
* Graphics)
*/
@Override
public void paint(Graphics g) {
RenderState rs = this.modelNode.getRenderState();
if ((rs != null) && (rs.getVisibility() != RenderState.VISIBILITY_VISIBLE)) {
// Just don't paint it.
return;
}
try {
this.prePaint(g);
Dimension size = this.getSize();
// TODO: No scrollbars
TableMatrix tm = this.tableMatrix;
tm.paint(g, size);
Collection<PositionedRenderable> prs = this.positionedRenderables;
if (prs != null) {
Iterator<PositionedRenderable> i = prs.iterator();
while (i.hasNext()) {
PositionedRenderable pr = i.next();
BoundableRenderable r = pr.getRenderable();
r.paintTranslated(g);
}
}
} finally {
// Must always call super implementation
super.paint(g);
}
}
/*
* (non-Javadoc)
*
* @see org.lobobrowser.html.renderer.BaseElementRenderable#doLayout(int,
* int, boolean)
*/
@Override
public void doLayout(int availWidth, int availHeight, boolean sizeOnly) {
Map<LayoutKey, LayoutValue> cachedLayout = this.cachedLayout;
RenderState rs = this.modelNode.getRenderState();
int whitespace = rs == null ? RenderState.WS_NORMAL : rs.getWhiteSpace();
Font font = rs == null ? null : rs.getFont();
// Having whiteSpace == NOWRAP and having a NOWRAP override
// are not exactly the same thing.
boolean overrideNoWrap = RenderThreadState.getState().overrideNoWrap;
LayoutKey layoutKey = new LayoutKey(availWidth, availHeight, whitespace, font, overrideNoWrap);
LayoutValue layoutValue;
if (sizeOnly) {
layoutValue = cachedLayout.get(layoutKey);
} else {
if (Objects.equals(layoutKey, this.lastLayoutKey)) {
layoutValue = this.lastLayoutValue;
} else {
layoutValue = null;
}
}
if (layoutValue == null) {
Collection<PositionedRenderable> prs = this.positionedRenderables;
if (prs != null) {
prs.clear();
}
this.otherOrdinal = 0;
this.clearGUIComponents();
this.clearDelayedPairs();
this.applyStyle(availWidth, availHeight);
TableMatrix tm = this.tableMatrix;
Insets insets = this.getInsets(false, false);
tm.reset(insets, availWidth, availHeight);
// TODO: No scrollbars
tm.build(availWidth, availHeight, sizeOnly);
tm.doLayout(insets);
// Import applicable delayed pairs.
// Only needs to be done if layout was
// forced. Otherwise, they should've
// been imported already.
Collection<?> pairs = this.delayedPairs;
if (pairs != null) {
Iterator<?> i = pairs.iterator();
while (i.hasNext()) {
DelayedPair pair = (DelayedPair) i.next();
if (pair.containingBlock == this) {
this.importDelayedPair(pair);
}
}
}
layoutValue = new LayoutValue(tm.getTableWidth(), tm.getTableHeight(),false,false);
if (sizeOnly) {
if (cachedLayout.size() > MAX_CACHE_SIZE) {
// Unlikely, but we should ensure it's bounded.
cachedLayout.clear();
}
cachedLayout.put(layoutKey, layoutValue);
this.lastLayoutKey = null;
this.lastLayoutValue = null;
} else {
this.lastLayoutKey = layoutKey;
this.lastLayoutValue = layoutValue;
}
}
this.width = layoutValue.getWidth();
this.height = layoutValue.getHeight();
this.sendGUIComponentsToParent();
this.sendDelayedPairsToParent();
}
/*
* (non-Javadoc)
*
* @see
* org.lobobrowser.html.renderer.BaseElementRenderable#invalidateLayoutLocal
* ()
*/
@Override
public void invalidateLayoutLocal() {
super.invalidateLayoutLocal();
this.cachedLayout.clear();
this.lastLayoutKey = null;
this.lastLayoutValue = null;
}
/*
* (non-Javadoc)
*
* @see
* org.lobobrowser.html.render.BoundableRenderable#getRenderablePoint(int,
* int)
*/
@Override
public RenderableSpot getLowestRenderableSpot(int x, int y) {
Collection<PositionedRenderable> prs = this.positionedRenderables;
if (prs != null) {
Iterator<PositionedRenderable> i = prs.iterator();
while (i.hasNext()) {
PositionedRenderable pr = i.next();
BoundableRenderable r = pr.getRenderable();
int childX = x - r.getX();
int childY = y - r.getY();
RenderableSpot rs = r.getLowestRenderableSpot(childX, childY);
if (rs != null) {
return rs;
}
}
}
RenderableSpot rs = this.tableMatrix.getLowestRenderableSpot(x, y);
if (rs != null) {
return rs;
}
return new RenderableSpot(this, x, y);
}
/*
* (non-Javadoc)
*
* @see
* org.lobobrowser.html.render.BoundableRenderable#onMouseClick(java.awt.
* event .MouseEvent, int, int)
*/
@Override
public boolean onMouseClick(MouseEvent event, int x, int y) {
Collection<PositionedRenderable> prs = this.positionedRenderables;
if (prs != null) {
Iterator<PositionedRenderable> i = prs.iterator();
while (i.hasNext()) {
PositionedRenderable pr = i.next();
BoundableRenderable r = pr.getRenderable();
Rectangle bounds = r.getBounds();
if (bounds.contains(x, y)) {
int childX = x - r.getX();
int childY = y - r.getY();
if (!r.onMouseClick(event, childX, childY)) {
return false;
}
}
}
}
return this.tableMatrix.onMouseClick(event, x, y);
}
/*
* (non-Javadoc)
*
* @see
* org.lobobrowser.html.renderer.BoundableRenderable#onDoubleClick(java.awt.
* event.MouseEvent, int, int)
*/
@Override
public boolean onDoubleClick(MouseEvent event, int x, int y) {
Collection<PositionedRenderable> prs = this.positionedRenderables;
if (prs != null) {
Iterator<PositionedRenderable> i = prs.iterator();
while (i.hasNext()) {
PositionedRenderable pr = i.next();
BoundableRenderable r = pr.getRenderable();
Rectangle bounds = r.getBounds();
if (bounds.contains(x, y)) {
int childX = x - r.getX();
int childY = y - r.getY();
if (!r.onDoubleClick(event, childX, childY)) {
return false;
}
}
}
}
return this.tableMatrix.onDoubleClick(event, x, y);
}
/*
* (non-Javadoc)
*
* @see
* org.lobobrowser.html.render.BoundableRenderable#onMouseDisarmed(java.awt
* .event.MouseEvent)
*/
@Override
public boolean onMouseDisarmed(MouseEvent event) {
return this.tableMatrix.onMouseDisarmed(event);
}
/*
* (non-Javadoc)
*
* @see
* org.lobobrowser.html.render.BoundableRenderable#onMousePressed(java.awt.
* event.MouseEvent, int, int)
*/
@Override
public boolean onMousePressed(MouseEvent event, int x, int y) {
Collection<PositionedRenderable> prs = this.positionedRenderables;
if (prs != null) {
Iterator<PositionedRenderable> i = prs.iterator();
while (i.hasNext()) {
PositionedRenderable pr = i.next();
BoundableRenderable r = pr.getRenderable();
Rectangle bounds = r.getBounds();
if (bounds.contains(x, y)) {
int childX = x - r.getX();
int childY = y - r.getY();
if (!r.onMousePressed(event, childX, childY)) {
return false;
}
}
}
}
return this.tableMatrix.onMousePressed(event, x, y);
}
/*
* (non-Javadoc)
*
* @see
* org.lobobrowser.html.render.BoundableRenderable#onMouseReleased(java.awt
* .event.MouseEvent, int, int)
*/
@Override
public boolean onMouseReleased(MouseEvent event, int x, int y) {
Collection<PositionedRenderable> prs = this.positionedRenderables;
if (prs != null) {
Iterator<PositionedRenderable> i = prs.iterator();
while (i.hasNext()) {
PositionedRenderable pr = i.next();
BoundableRenderable r = pr.getRenderable();
Rectangle bounds = r.getBounds();
if (bounds.contains(x, y)) {
int childX = x - r.getX();
int childY = y - r.getY();
if (!r.onMouseReleased(event, childX, childY)) {
return false;
}
}
}
}
return this.tableMatrix.onMouseReleased(event, x, y);
}
@Override
public boolean onKeyPressed(KeyEvent event) {
ModelNode me = this.modelNode;
if (me != null) {
return HtmlController.getInstance().onKeyPress(this.modelNode, event);
} else {
return true;
}
}
@Override
public boolean onKeyUp(KeyEvent event) {
ModelNode me = this.modelNode;
if (me != null) {
return HtmlController.getInstance().onKeyUp(this.modelNode, event);
} else {
return true;
}
}
@Override
public boolean onKeyDown(KeyEvent event) {
ModelNode me = this.modelNode;
if (me != null) {
return HtmlController.getInstance().onKeyDown(this.modelNode, event);
} else {
return true;
}
}
/*
* (non-Javadoc)
*
* @see org.lobobrowser.html.render.RCollection#getRenderables()
*/
@Override
public Iterator<BoundableRenderable> getRenderables() {
Collection<PositionedRenderable> prs = this.positionedRenderables;
if (prs != null) {
Collection<BoundableRenderable> c = new LinkedList<BoundableRenderable>();
Iterator<PositionedRenderable> i = prs.iterator();
while (i.hasNext()) {
PositionedRenderable pr = i.next();
BoundableRenderable r = pr.getRenderable();
c.add(r);
}
Iterator<BoundableRenderable> i2 = this.tableMatrix.getRenderables();
while (i2.hasNext()) {
c.add(i2.next());
}
return c.iterator();
} else {
return this.tableMatrix.getRenderables();
}
}
/*
* (non-Javadoc)
*
* @see
* org.lobobrowser.html.dombl.UINode#repaint(org.lobobrowser.html.dombl.
* ModelNode )
*/
@Override
public void repaint(ModelNode modelNode) {
// NOP
}
/*
* (non-Javadoc)
*
* @see org.lobobrowser.html.render.RenderableContainer#getBackground()
*/
@Override
public Color getPaintedBackgroundColor() {
return this.container.getPaintedBackgroundColor();
}
/**
* Adds the positioned renderable.
*
* @param renderable
* the renderable
* @param verticalAlignable
* the vertical alignable
* @param isFloat
* the is float
*/
private final void addPositionedRenderable(BoundableRenderable renderable, boolean verticalAlignable,
boolean isFloat) {
// Expected to be called only in GUI thread.
SortedSet<PositionedRenderable> others = this.positionedRenderables;
if (others == null) {
others = new TreeSet<PositionedRenderable>(new ZIndexComparator());
this.positionedRenderables = others;
}
others.add(new PositionedRenderable(renderable, verticalAlignable, this.otherOrdinal++, isFloat));
renderable.setParent(this);
if (renderable instanceof RUIControl) {
this.container.addComponent(((RUIControl) renderable).getWidget().getComponent());
}
}
/**
* Import delayed pair.
*
* @param pair
* the pair
*/
private void importDelayedPair(DelayedPair pair) {
pair.positionPairChild();
BoundableRenderable r = pair.child;
this.addPositionedRenderable(r, false, false);
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "RTable[this=" + System.identityHashCode(this) + ",node=" + this.modelNode + "]";
}
}