/* 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 Apr 17, 2005 */ package org.lobobrowser.html.control; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Insets; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.lobobrowser.html.dombl.ModelNode; import org.lobobrowser.html.dombl.UINode; import org.lobobrowser.html.domimpl.DOMNodeImpl; import org.lobobrowser.html.layout.LayoutKey; import org.lobobrowser.html.layout.LayoutValue; import org.lobobrowser.html.renderer.BaseElementRenderable; import org.lobobrowser.html.renderer.FrameContext; import org.lobobrowser.html.renderer.HtmlController; import org.lobobrowser.html.renderer.RBlockViewport; import org.lobobrowser.html.renderer.RElement; import org.lobobrowser.html.renderer.RenderableContainer; import org.lobobrowser.html.renderer.RenderableSpot; import org.lobobrowser.html.renderer.UIControl; import org.lobobrowser.html.renderstate.RenderState; import org.lobobrowser.http.UserAgentContext; import org.lobobrowser.util.Objects; /** * The Class RUIControl. * * @author J. H. S. */ public class RUIControl extends BaseElementRenderable implements RElement { /** The Constant MAX_CACHE_SIZE. */ private static final int MAX_CACHE_SIZE = 10; /** The widget. */ private UIControl widget; /** The model node. */ protected final ModelNode modelNode; /** The frame context. */ private final FrameContext frameContext; /** The declared width. */ private int declaredWidth = -1; /** The declared height. */ private int declaredHeight = -1; /** The last layout key. */ private LayoutKey lastLayoutKey = null; /** The last layout value. */ private LayoutValue lastLayoutValue = null; /** The cached layout. */ private final Map<LayoutKey, LayoutValue> cachedLayout = new HashMap<LayoutKey, LayoutValue>(5); /** * Instantiates a new RUI control. * * @param me * the me * @param widget * the widget * @param container * the container * @param frameContext * the frame context * @param ucontext * the ucontext */ public RUIControl(ModelNode me, UIControl widget, RenderableContainer container, FrameContext frameContext, UserAgentContext ucontext) { super(container, me, ucontext); this.modelNode = me; this.widget = widget; this.frameContext = frameContext; widget.setRUIControl(this); } /* * (non-Javadoc) * * @see org.lobobrowser.html.renderer.BaseRCollection#focus() */ @Override public void focus() { super.focus(); Component c = this.widget.getComponent(); c.requestFocus(); } /* * (non-Javadoc) * * @see * org.lobobrowser.html.renderer.BaseElementRenderable#invalidateLayoutLocal * () */ @Override public final void invalidateLayoutLocal() { // Invalidate widget (some redundancy) super.invalidateLayoutLocal(); this.widget.invalidate(); // Invalidate cached values this.cachedLayout.clear(); this.lastLayoutKey = null; this.lastLayoutValue = null; } /* * (non-Javadoc) * * @see org.lobobrowser.html.renderer.RElement#getVAlign() */ @Override public int getVAlign() { return this.widget.getVAlign(); } /** * Checks for background. * * @return true, if successful */ public boolean hasBackground() { return (this.backgroundColor != null) || (this.backgroundImage != null) || (this.lastBackgroundImageUri != null); } /* * (non-Javadoc) * * @see org.lobobrowser.html.renderer.BaseElementRenderable#paint(java.awt. * Graphics) */ @Override public final void paint(Graphics g) { RenderState rs = this.modelNode.getRenderState(); if ((rs != null) && (rs.getVisibility() != RenderState.VISIBILITY_VISIBLE)) { // Just don't paint it. return; } this.prePaint(g); Insets insets = this.getBorderInsets(); g.translate(insets.left, insets.top); try { this.widget.paint(g); } finally { g.translate(-insets.left, -insets.top); } } /* * (non-Javadoc) * * @see * org.lobobrowser.html.renderer.BoundableRenderable#onMouseClick(java.awt. * event .MouseEvent, int, int) */ @Override public boolean onMouseClick(MouseEvent event, int x, int y) { ModelNode me = this.modelNode; if (me != null) { return HtmlController.getInstance().onMouseClick(me, event, null, x, y); } else { return true; } } /* * (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) { ModelNode me = this.modelNode; if (me != null) { return HtmlController.getInstance().onDoubleClick(me, event, null, x, y); } else { return true; } } /* * (non-Javadoc) * * @see * org.lobobrowser.html.renderer.BoundableRenderable#onMousePressed(java.awt * .event.MouseEvent, int, int) */ @Override public boolean onMousePressed(MouseEvent event, int x, int y) { ModelNode me = this.modelNode; if (me != null) { return HtmlController.getInstance().onMouseDown(me, event, null, x, y); } else { return true; } } /* * (non-Javadoc) * * @see * org.lobobrowser.html.renderer.BoundableRenderable#onMouseReleased(java. * awt .event.MouseEvent, int, int) */ @Override public boolean onMouseReleased(MouseEvent event, int x, int y) { ModelNode me = this.modelNode; if (me != null) { return HtmlController.getInstance().onMouseUp(me, event, null, x, y); } else { return true; } } /* * (non-Javadoc) * * @see * org.lobobrowser.html.renderer.BoundableRenderable#onMouseDisarmed(java. * awt .event.MouseEvent) */ @Override public boolean onMouseDisarmed(MouseEvent event) { ModelNode me = this.modelNode; if (me != null) { return HtmlController.getInstance().onMouseDisarmed(me, event); } else { return true; } } @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.BoundableRenderable#invalidateState(org. * xamjwg .html.renderer.RenderableContext) */ /** * Invalidate render style. */ public void invalidateRenderStyle() { // NOP - No RenderStyle below this node. } /* * (non-Javadoc) * * @see * org.lobobrowser.html.dombl.UINode(org.lobobrowser.html.dombl.ModelNode * modelNode); */ @Override public void repaint(ModelNode modelNode) { Object widget = this.widget; if (widget instanceof UINode) { ((UINode) widget).repaint(modelNode); } else { this.repaint(); } } /* * (non-Javadoc) * * @see * org.lobobrowser.html.renderer.BaseRCollection#updateWidgetBounds(int, * int) */ @Override public void updateWidgetBounds(int guiX, int guiY) { // Overrides super.updateWidgetBounds(guiX, guiY); Insets insets = this.getBorderInsets(); this.widget.setBounds(guiX + insets.left, guiY + insets.top, this.width - insets.left - insets.right, this.height - insets.top - insets.bottom); } /* * (non-Javadoc) * * @see org.lobobrowser.html.renderer.BaseBoundableRenderable# * getBlockBackgroundColor () */ @Override public Color getBlockBackgroundColor() { return this.widget.getBackgroundColor(); } /* * (non-Javadoc) * * @see * org.lobobrowser.html.render.BoundableRenderable#paintSelection(java.awt. * Graphics, boolean, org.lobobrowser.html.render.RenderablePoint, * org.lobobrowser.html.render.RenderablePoint) */ @Override public boolean paintSelection(Graphics g, boolean inSelection, RenderableSpot startPoint, RenderableSpot endPoint) { inSelection = super.paintSelection(g, inSelection, startPoint, endPoint); if (inSelection) { Color over = new Color(0, 0, 255, 50); if (over != null) { Color oldColor = g.getColor(); try { g.setColor(over); g.fillRect(0, 0, this.width, this.height); } finally { g.setColor(oldColor); } } } return inSelection; } /* * (non-Javadoc) * * @see * org.lobobrowser.html.renderer.BaseRCollection#extractSelectionText(java. * lang .StringBuffer, boolean, * org.lobobrowser.html.renderer.RenderableSpot, * org.lobobrowser.html.renderer.RenderableSpot) */ @Override public boolean extractSelectionText(StringBuffer buffer, boolean inSelection, RenderableSpot startPoint, RenderableSpot endPoint) { // No text here return inSelection; } /* * (non-Javadoc) * * @see * org.lobobrowser.html.renderer.BoundableRenderable#getLowestRenderableSpot * (int, int) */ @Override public RenderableSpot getLowestRenderableSpot(int x, int y) { // Nothing draggable - return self return new RenderableSpot(this, x, y); } /* * (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(); LayoutKey layoutKey = new LayoutKey(availWidth, availHeight, whitespace, font,false); LayoutValue layoutValue; if (sizeOnly) { layoutValue = cachedLayout.get(layoutKey); } else { if (Objects.equals(this.lastLayoutKey, layoutKey)) { layoutValue = this.lastLayoutValue; } else { layoutValue = null; } } if (layoutValue == null) { this.applyStyle(availWidth, availHeight); RenderState renderState = this.modelNode.getRenderState(); Insets paddingInsets = this.paddingInsets; if (paddingInsets == null) { paddingInsets = RBlockViewport.ZERO_INSETS; } Insets borderInsets = this.borderInsets; if (borderInsets == null) { borderInsets = RBlockViewport.ZERO_INSETS; } Insets marginInsets = this.marginInsets; if (marginInsets == null) { marginInsets = RBlockViewport.ZERO_INSETS; } int actualAvailWidth = availWidth - paddingInsets.left - paddingInsets.right - borderInsets.left - borderInsets.right - marginInsets.left - marginInsets.right; int actualAvailHeight = availHeight - paddingInsets.top - paddingInsets.bottom - borderInsets.top - borderInsets.bottom - marginInsets.top - marginInsets.bottom; Integer dw = this.getDeclaredWidth(renderState, actualAvailWidth); Integer dh = this.getDeclaredHeight(renderState, actualAvailHeight); int declaredWidth = dw == null ? -1 : dw.intValue(); int declaredHeight = dh == null ? -1 : dh.intValue(); this.declaredWidth = declaredWidth; this.declaredHeight = declaredHeight; UIControl widget = this.widget; widget.reset(availWidth, availHeight); Insets insets = this.getInsets(false, false); int finalWidth = declaredWidth == -1 ? -1 : declaredWidth - insets.left - insets.right; int finalHeight = declaredHeight == -1 ? -1 : declaredHeight - insets.top - insets.bottom; if ((finalWidth == -1) || (finalHeight == -1)) { Dimension size = widget.getPreferredSize(); if (finalWidth == -1) { finalWidth = size.width + insets.left + insets.right; } if (finalHeight == -1) { finalHeight = size.height + insets.top + insets.bottom; } } layoutValue = new LayoutValue(finalWidth, finalHeight,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(); } /** * May be called by controls when they wish to modifiy their preferred size * (e.g. an image after it's loaded). This method must be called in the GUI * thread. */ public final void preferredSizeInvalidated() { int dw = RUIControl.this.declaredWidth; int dh = RUIControl.this.declaredHeight; if ((dw == -1) || (dh == -1)) { this.frameContext.delayedRelayout((DOMNodeImpl) this.modelNode); } else { RUIControl.this.repaint(); } } /* * (non-Javadoc) * * @see org.lobobrowser.html.renderer.RCollection#getRenderables() */ @Override public Iterator<?> getRenderables() { // No children for GUI controls return null; } /* * (non-Javadoc) * * @see org.lobobrowser.html.renderer.RenderableContainer# * getPaintedBackgroundColor() */ @Override public Color getPaintedBackgroundColor() { return this.container.getPaintedBackgroundColor(); } /** * Gets the foreground color. * * @return the foreground color */ public Color getForegroundColor() { RenderState rs = this.modelNode.getRenderState(); return rs == null ? null : rs.getColor(); } /** * Gets the background color. * * @return the background color */ public Color getBackgroundColor() { return this.backgroundColor; } /** * Gets the widget. * * @return the widget */ public UIControl getWidget() { return widget; } /** * Sets the widget. * * @param widget * the new widget */ public void setWidget(UIControl widget) { this.widget = widget; } }