X-Git-Url: http://source.jalview.org/gitweb/?p=jalviewjs.git;a=blobdiff_plain;f=site%2Fj2s%2Fswingjs%2Fplaf%2FJSComponentUI.java;fp=site%2Fj2s%2Fswingjs%2Fplaf%2FJSComponentUI.java;h=d4f6419793dc9eae37e30f7a5ad051d73a477ebb;hp=39c525307475b304b7711ac5d827224fc5204052;hb=b9b7a352eee79b7764c3b09c9d19663075061d8c;hpb=7301a2415adab88038b291fc54caeeb3a5a47a44 diff --git a/site/j2s/swingjs/plaf/JSComponentUI.java b/site/j2s/swingjs/plaf/JSComponentUI.java index 39c5253..d4f6419 100644 --- a/site/j2s/swingjs/plaf/JSComponentUI.java +++ b/site/j2s/swingjs/plaf/JSComponentUI.java @@ -1,880 +1,880 @@ -package swingjs.plaf; - -import jsjava.awt.AWTEvent; -import jsjava.awt.Color; -import jsjava.awt.Component; -import jsjava.awt.Dimension; -import jsjava.awt.Font; -import jsjava.awt.FontMetrics; -import jsjava.awt.Graphics; -import jsjava.awt.GraphicsConfiguration; -import jsjava.awt.Image; -import jsjava.awt.Insets; -import jsjava.awt.Point; -import jsjava.awt.Rectangle; -import jsjava.awt.Toolkit; -import jsjava.awt.event.FocusEvent; -import jsjava.awt.event.PaintEvent; -import jsjava.awt.image.ColorModel; -import jsjava.awt.image.ImageObserver; -import jsjava.awt.image.ImageProducer; -import jsjava.awt.image.VolatileImage; -import jsjava.awt.peer.ContainerPeer; -import jsjava.awt.peer.LightweightPeer; -import jsjavax.swing.AbstractButton; -import jsjavax.swing.JComponent; -import jsjavax.swing.JRootPane; -import jsjavax.swing.plaf.ComponentUI; -import jssun.awt.CausedFocusEvent.Cause; -import swingjs.JSToolkit; -import swingjs.api.DOMNode; -import swingjs.api.JQueryObject; - -/** - * The JSComponentUI subclasses are where all the detailed HTML5 implementation is - * carried out. These subclasses mirror the subclasses found in the actual javax.swing.plaf - * but have an important difference in that that effectively act as both the UI (a single - * implementation for a given AppContext in Swing) and a peer (one implementation per component). - * - * So here we store both the constants for the HTML5 "LookAndFeel", but also - * HTML5 objects that really are on the page. - * - * Essentially, at least for now, we are not implementing the HTML5LookAndFeel as such. We'll see how that goes. - * - * - * - * @author Bob Hanson - * - */ -public abstract class JSComponentUI extends ComponentUI implements JSEventHandler { - - /** - * provides a unique id for any component; set on instantiation - */ - protected static int incr; - - - /** - * a unique id - */ - protected String id; - - /** - * the associated JComponent; for which this is c.ui - * - */ - protected JComponent c; - - - /** - * the outermost div holding a component -- left, top, and for a container width and height - */ - protected DOMNode outerNode; - - /** - * the main object for the component, possibly containing others, such as radio button with its label - */ - protected DOMNode domNode; - - /** - * a component or subcomponent that can be enabled/disabled - */ - protected DOMNode enableNode; - - /** - * the part of a component that can hold text - */ - protected DOMNode textNode; - - /** - * the subcomponent with the value field - */ - protected DOMNode valueNode; - - /** - * a component that is being scrolled by a JScrollPane - */ - protected DOMNode scrollNode; - - - /** - * a component that is focusable - */ - protected DOMNode focusNode; - - - /** - * DOM components pre-defined (JScrollPane) - * - */ - protected Component[] components; - - /** - * a numerical reference for an ID - */ - protected int num; - - /** - * not implemented/needed currently. Java handles this nicely - * - */ - protected boolean isTainted = true; - - /** - * left and top coordinates - */ - protected int x, y; - - /** - * preferred dimension set by user - * - */ - protected Dimension preferredSize; - - - /** - * panels - * - */ - protected boolean isContainer; - - /** - * linked nodes of this class - * - */ - protected JSComponentUI parent; - - - String currentText; - - - /** - * the scroller for a text area - */ - protected JSScrollPaneUI scrollerNode; - - - /** - * uiClassID for this component - */ - protected String classID; - - - private DOMNode document, body; - - - protected boolean needPreferred; - - - - public JSComponentUI() { - setDoc(); - } - - protected void setDoc() { - /** - * @j2sNative - * - * this.document = document; - * this.body = document.body; - */ - {} - } - - protected abstract void installJSUI(); - protected abstract void uninstallJSUI(); - - public void installUI(JComponent c) { - // already done installJSUI(); - } - - public void uninstallUI(JComponent c) { - uninstallJSUI(); - } - - protected JQueryObject $(DOMNode node) { - return JSToolkit.getJQuery().$(node); - } - - /** - * mark this component as in need of update; - * maybe not necessary, though. It comes after the value callback - */ - public void setTainted() { - isTainted = true; - } - - public abstract DOMNode getDOMObject(); - - public JSComponentUI set(JComponent target) { - c = target; - newID(); - if (needPreferred) - getPreferredSize(c); - installJSUI(); // need to do this immediately, not later - return this; - } - - protected void newID() { - classID = c.getUIClassID(); - if (id == null) { - num = ++incr; - id = c.getHTMLName(classID) + "_" + num; - } - } - - protected DOMNode setCssFont(DOMNode obj, Font font) { - if (font != null) { - int istyle = font.getStyle(); - String name = font.getFamily(); - if (name == "Dialog") - name = "Arial"; - DOMNode.setStyles(obj, "font-family", name, "font-size", - font.getSize() + "px", "font-style", - ((istyle & Font.ITALIC) == 0 ? "normal" : "italic"), "font-weight", - ((istyle & Font.BOLD) == 0 ? "normal" : "bold")); - } - if (c.isBackgroundSet()) - setBackground(c.getBackground()); - setForeground(c.getForeground()); - return obj; - } - - protected DOMNode createDOMObject(String key, String id, String... attr) { - DOMNode obj = DOMNode.createElement(key, id); - for (int i = 0; i < attr.length;) - DOMNode.setAttr(obj, attr[i++], attr[i++]); - if (!c.isEnabled()) - setEnabled(false); - return obj; - } - - /** - * JSmolCore.js will look for data-UI attribute and, if found, reroute directly here - * @param node - */ - protected void bindMouse(DOMNode node) { - DOMNode.setAttr(node, "data-UI", this); - } - - /** - * called by JmolCore.js - * @return true if handled - */ - public boolean handleJSEvent(Object target, int eventType, Object jQueryEvent) { - //System.out.println(id + " handling event " + eventType + jQueryEvent); - return false; - } - - protected DOMNode wrap(String type, String id, DOMNode... elements) { - return append(createDOMObject(type, id + type), elements); - } - - protected DOMNode append(DOMNode obj, DOMNode[] elements) { - for (int i = 0; i < elements.length; i++) { - obj.appendChild(elements[i]); - } - return obj; - } - - protected void debugDump(DOMNode d) { - System.out.println(DOMNode.getAttr(d, "outerHTML")); - } - - protected static void vCenter(DOMNode obj, int offset) { - DOMNode.setStyles(obj, - "top", "50%", - "transform","translateY(" + offset + "%)"); - } - - /** - * overloaded to allow panel and radiobutton to handle slightly differently - * - * @param obj - * @param addCSS - * @return - */ - protected Dimension setHTMLSize(DOMNode obj, boolean addCSS) { - return setHTMLSize1(obj, addCSS, true); - } - - /** - * also called by JSRadioButtonUI so that it can calculate - * subset dimensions - * - * @param node - * @param addCSS - * @param usePreferred - * @return - */ - @SuppressWarnings("unused") - protected Dimension setHTMLSize1(DOMNode node, boolean addCSS, boolean usePreferred) { - if (node == null) - return null; - int h, w; - String w0 = null, h0 = null; - DOMNode parentNode = null; - - if (scrollerNode != null) { - w = scrollerNode.c.getWidth(); - h = scrollerNode.c.getHeight(); - } else if (usePreferred && preferredSize != null) { - // user has set preferred size - w = preferredSize.width; - h = preferredSize.height; - } else { - // determine the natural size of this object - // save the parent node -- we will need to reset that. - parentNode = DOMNode.remove(node); - - // remove position, width, and height, because those are what we are - // setting here - /** - * @j2sNative - * - * w0 = node.style.width; - * h0 = node.style.height; - */ - {} - DOMNode.setStyles(node, "position", null, "width", null, "height", null); - DOMNode div; - if (DOMNode.getAttr(node, "tagName") == "DIV") - div = node; - else - div = wrap("div", id + "_temp", node); - DOMNode.setStyles(div, "position", "absolute"); - - // process of discovering width and height is facilitated using jQuery - // and appending to document.body. - - body.appendChild(div); - - //System.out.println(DOMNode.getAttr(node, "outerHTML")); - w = (int) Math.ceil($(div).width() + 0.5); - h = (int) Math.ceil($(div).height() + 0.5); - body.removeChild(div); - } - - Dimension size = getCSSDimension(w, h); - if (addCSS) { - DOMNode.setStyles(node, "position", "absolute"); - DOMNode.setSize(node, size.width, size.height); - } else { - DOMNode.setStyles(node, "position", null); - // check to reset width/height after getPreferredSize - if (w0 != null) - DOMNode.setStyles(node, "width", w0, "height", h0); - } - if (parentNode != null) - parentNode.appendChild(node); - //System.out.println("JSComponentUI " + id + " resized to " + w + "x" + h + " parent=" + DOMNode.getAttr(parentNode,"id")); - return size; - } - - /** - * can be overloaded to allow some special adjustments - * - * @param w - * @param h - * @return - */ - protected Dimension getCSSDimension(int w, int h) { - return new Dimension(w, h); - } - - /** - * creates the DOM node and inserts it into the tree at the correct place, - * iterating through all children if this is a container - * - * @return - * - */ - protected DOMNode setHTMLElement() { - if (!isTainted) - return outerNode; - - // check for root pane -- not included in DOM - JRootPane root = (isContainer ? c.getRootPane() : null); - if (c == root) { - isTainted = false; - return outerNode; - } - - domNode = getDOMObject(); - - // divObj will need recreating if a propertyChange event has occurred - // check for content pane -- needs to be added to the HTML5 content layer - // div - - // needs some work for changes after applet creation - - if (outerNode == null) { - outerNode = wrap("div", id, domNode); - if (root != null && root.getContentPane() == c) - swingjs.JSToolkit.getHTML5Applet(c)._getContentLayer() - .appendChild(outerNode); - } - - // set position - - DOMNode.setStyles(outerNode, "position", "absolute", "left", (x = c.getX()) - + "px", "top", (y = c.getY()) + "px"); - - if (isContainer) { - - // set width from component - - System.out.println("JSComponentUI container " + id + " " + c.getBounds()); - DOMNode.setSize(outerNode, c.getWidth(), c.getHeight()); - - // add all children - Component[] children = (components == null ? c.getComponents() - : components); - for (int i = children.length; --i >= 0;) { - JSComponentUI ui = JSToolkit.getUI(children[i], false); - if (ui == null) { - // Box.Filler has no ui. - continue; - } - if (ui.outerNode == null) - ui.setHTMLElement(); - if (ui.outerNode == null) { - System.out.println("JSCUI could not add " + ui.c.getName() + " to " - + c.getName()); - } else { - outerNode.appendChild(ui.outerNode); - } - ui.parent = this; - } - } - - // mark as not tainted - // debugDump(divObj); - isTainted = false; - return outerNode; - } - - /** - * c ignored because JSComponentUI is one per component - */ - public Dimension getPreferredSize(JComponent c) { - //System.out.println("getPreferredSize for " + id + " " + c.getName()); - Dimension d = setHTMLSize(getDOMObject(), false); - //System.out.println("JSComponentUI " + id + " getting preferred size as " + d); - return d; - } - - public void paint(Graphics g, JComponent c) { - // Note that for now, button graphics - // are BEHIND the button. We will need to paint onto the - // glass pane for this to work, and then also manage - // mouse clicks and key clicks with that in mind. - if (c.isOpaque()) { - g.setColor(c.getBackground()); - g.fillRect(0, 0, c.getWidth(), c.getHeight()); - } - } - - public void update(Graphics g, JComponent c) { - // called from JComponent.paintComponent - boolean testing = false;//true; - if (testing) { - g.setColor(Color.red); - g.drawRect(0, 0, c.getWidth(), c.getHeight()); - System.out.println("drawing " + c.getWidth() + " " + c.getHeight()); - } - setHTMLElement(); - paint(g, c); - } - - public Dimension getMinimumSize(JComponent c) { - return getPreferredSize(c); - } - - public Dimension getMaximumSize(JComponent c) { - return null;// getPreferredSize(c); - } - - /** - * Returns true if the specified x,y location is - * contained within the look and feel's defined shape of the specified - * component. x and y are defined to be relative - * to the coordinate system of the specified component. Although - * a component's bounds is constrained to a rectangle, - * this method provides the means for defining a non-rectangular - * shape within those bounds for the purpose of hit detection. - * - * @param c the component where the x,y location is being queried; - * this argument is often ignored, - * but might be used if the UI object is stateless - * and shared by multiple components - * @param x the x coordinate of the point - * @param y the y coordinate of the point - * - * @see jsjavax.swing.JComponent#contains - * @see jsjava.awt.Component#contains - */ - public boolean contains(JComponent c, int x, int y) { - return c.inside(x, y); - } - - /** - * Returns an instance of the UI delegate for the specified component. - * Each subclass must provide its own static createUI - * method that returns an instance of that UI delegate subclass. - * If the UI delegate subclass is stateless, it may return an instance - * that is shared by multiple components. If the UI delegate is - * stateful, then it should return a new instance per component. - * The default implementation of this method throws an error, as it - * should never be invoked. - */ - public static ComponentUI createUI(JComponent c) { - // SwingJS so, actually, we don't do this. This class is NOT stateless. - // Instead, what we do is to create a unique instance - // right in UIManager. The sequence is: - // JRadioButton.updateUI() - // --> jsjavax.swing.UIManager.getUI(this) - // --> jsjavax.swing.UIManager.getDefaults().getUI(target) - // --> JSToolkit.getComponentUI(target) - // --> creates an instance of JRadioButtonUI and returns - // that instance as JRadioButton.ui, which is NOT static. - // -// throw new Error("ComponentUI.createUI not implemented."); - return null; - } - - /** - * Returns the baseline. The baseline is measured from the top of - * the component. This method is primarily meant for - * LayoutManagers to align components along their - * baseline. A return value less than 0 indicates this component - * does not have a reasonable baseline and that - * LayoutManagers should not align this component on - * its baseline. - *

- * This method returns -1. Subclasses that have a meaningful baseline - * should override appropriately. - * - * @param c JComponent baseline is being requested for - * @param width the width to get the baseline for - * @param height the height to get the baseline for - * @throws NullPointerException if c is null - * @throws IllegalArgumentException if width or height is < 0 - * @return baseline or a value < 0 indicating there is no reasonable - * baseline - * @see jsjavax.swing.JComponent#getBaseline(int,int) - * @since 1.6 - */ - public int getBaseline(JComponent c, int width, int height) { - if (c == null) { - throw new NullPointerException("Component must be non-null"); - } - if (width < 0 || height < 0) { - throw new IllegalArgumentException( - "Width and height must be >= 0"); - } - return -1; - } - - /** - * Returns an enum indicating how the baseline of he component - * changes as the size changes. This method is primarily meant for - * layout managers and GUI builders. - *

- * This method returns BaselineResizeBehavior.OTHER. - * Subclasses that support a baseline should override appropriately. - * - * @param c JComponent to return baseline resize behavior for - * @return an enum indicating how the baseline changes as the component - * size changes - * @throws NullPointerException if c is null - * @see jsjavax.swing.JComponent#getBaseline(int, int) - * @since 1.6 - */ - public Component.BaselineResizeBehavior getBaselineResizeBehavior( - JComponent c) { - if (c == null) { - throw new NullPointerException("Component must be non-null"); - } - return Component.BaselineResizeBehavior.OTHER; - } - - /** - * overridden in JSPasswordFieldUI - * @return texat - */ - public String getJSTextValue() { - return (String) DOMNode.getAttr(domNode, valueNode == null ? "innerHTML" : "value"); - } - - public void notifyPropertyChanged(String prop) { - DOMNode obj = null; - String val = null; - if (prop == "text") { - val = ((AbstractButton) c).getText(); - if (val.equals(currentText)) // we set it here, then fired the property change - return; - currentText = val; - if (textNode != null) { - prop = "innerHTML"; - obj = textNode; - } else if (valueNode != null) { - prop = "value"; - obj = valueNode; - } - } else if (prop == "preferredSize") { - preferredSize = c.getPreferredSize(); // may be null - getPreferredSize(); - return; - } - if (obj == null) { - System.out.println("JSComponentUI: unrecognized prop: " + prop); - } else { - System.out.println("JSComponentUI: setting " + id + " " + prop);// + " " + val); - setProp(obj, prop, val); - } - } - - protected DOMNode setProp(DOMNode obj, String prop, String val) { - return DOMNode.setAttr(obj, prop, val); - } - - @Override - public boolean isObscured() { - JSToolkit.notImplemented(""); - return false; - } - - @Override - public boolean canDetermineObscurity() { - JSToolkit.notImplemented(""); - return false; - } - - @Override - public void setVisible(boolean b) { - DOMNode.setStyles(outerNode, "display", b ? "block" : "none"); - } - - @Override - public void setEnabled(boolean b) { - if (enableNode != null) - DOMNode.setAttr(enableNode, "disabled", (b ? null : "TRUE")); - } - - @Override - public void paint(Graphics g) { - // nothing to do here - } - - @Override - public void repaint(long tm, int x, int y, int width, int height) { - // nothing to do here - } - - @Override - public void print(Graphics g) { - JSToolkit.notImplemented(""); - } - - @Override - public void setBounds(int x, int y, int width, int height, int op) { - switch (op) { - case SET_SIZE: - case SET_BOUNDS: - case SET_CLIENT_SIZE: - if (scrollerNode != null) { - width = Math.min(width, scrollerNode.c.getWidth()); - height = Math.min(height, scrollerNode.c.getHeight()); - } - System.out.println(id + " setBounds " + x + " " + y + " " + width + " " + height + " op=" + op); - if (domNode != null) - DOMNode.setSize(domNode, width, height); - break; - } - } - - @Override - public void handleEvent(AWTEvent e) { - JSToolkit.notImplemented(""); - - } - - @Override - public void coalescePaintEvent(PaintEvent e) { - JSToolkit.notImplemented(""); - - } - - /** - * Coordinates relative to the document - * - */ - @Override - public Point getLocationOnScreen() { - Insets offset = (Insets) $(outerNode).offset(); - return new Point(offset.left, offset.top); - } - - @Override - public Dimension getPreferredSize() { - return getPreferredSize(c); - } - - @Override - public Dimension getMinimumSize() { - JSToolkit.notImplemented(""); - return getPreferredSize(c); - } - - @Override - public ColorModel getColorModel() { - return Toolkit.getDefaultToolkit().getColorModel(); - } - - @Override - public Toolkit getToolkit() { - return Toolkit.getDefaultToolkit(); - } - - @Override - public Graphics getGraphics() { - // n/a -- called from java.awt.Component when NOT a LightweightPeer. - return null; - } - - @Override - public FontMetrics getFontMetrics(Font font) { - return c.getFontMetrics(font); - } - - @Override - public void dispose() { - JSToolkit.notImplemented(""); - } - - @Override - public void setForeground(Color color) { - if (domNode != null) - DOMNode.setStyles(domNode, "color", JSToolkit.getCSSColor(color == null ? Color.black : color)); - } - - @Override - public void setBackground(Color color) { - if (domNode != null) - DOMNode.setStyles(domNode, "background-color", JSToolkit.getCSSColor(color == null ? Color.white : color)); - } - - @Override - public void setFont(Font f) { - if (domNode != null) - setCssFont(domNode, f); - } - - @Override - public void updateCursorImmediately() { - JSToolkit.notImplemented(""); - } - - @Override - public boolean requestFocus(Component lightweightChild, boolean temporary, - boolean focusedWindowChangeAllowed, long time, Cause cause) { - if (focusNode == null) - return false; - $(focusNode).focus(); - if (textNode != null) - $(textNode).select(); - return true; - } - - @Override - public boolean isFocusable() { - return (focusNode != null); - } - - @Override - public Image createImage(ImageProducer producer) { - JSToolkit.notImplemented(""); - return null; - } - - @Override - public Image createImage(int width, int height) { - JSToolkit.notImplemented(""); - return null; - } - - @Override - public VolatileImage createVolatileImage(int width, int height) { - JSToolkit.notImplemented(""); - return null; - } - - @Override - public boolean prepareImage(Image img, int w, int h, ImageObserver o) { - JSToolkit.notImplemented(""); - return false; - } - - @Override - public int checkImage(Image img, int w, int h, ImageObserver o) { - JSToolkit.notImplemented(""); - return 0; - } - - @Override - public GraphicsConfiguration getGraphicsConfiguration() { - JSToolkit.notImplemented(""); - return null; - } - - @Override - public boolean handlesWheelScrolling() { - JSToolkit.notImplemented(""); - return false; - } - - @Override - public Image getBackBuffer() { - JSToolkit.notImplemented(""); - return null; - } - - @Override - public void destroyBuffers() { - JSToolkit.notImplemented(""); - - } - - @Override - public void reparent(ContainerPeer newContainer) { - JSToolkit.notImplemented(""); - - } - - @Override - public boolean isReparentSupported() { - JSToolkit.notImplemented(""); - return false; - } - - @Override - public void layout() { - JSToolkit.notImplemented(""); - - } - - @Override - public Rectangle getBounds() { - JSToolkit.notImplemented(""); - return null; - } - - public boolean hasFocus() { - return focusNode != null && focusNode == DOMNode.getAttr(document, "activeElement"); - } - - public void notifyFocus(boolean focusGained) { - Toolkit.getEventQueue().postEvent(new FocusEvent(c, focusGained ? FocusEvent.FOCUS_GAINED : FocusEvent.FOCUS_LOST)); - } -} +package swingjs.plaf; + +import jsjava.awt.AWTEvent; +import jsjava.awt.Color; +import jsjava.awt.Component; +import jsjava.awt.Dimension; +import jsjava.awt.Font; +import jsjava.awt.FontMetrics; +import jsjava.awt.Graphics; +import jsjava.awt.GraphicsConfiguration; +import jsjava.awt.Image; +import jsjava.awt.Insets; +import jsjava.awt.Point; +import jsjava.awt.Rectangle; +import jsjava.awt.Toolkit; +import jsjava.awt.event.FocusEvent; +import jsjava.awt.event.PaintEvent; +import jsjava.awt.image.ColorModel; +import jsjava.awt.image.ImageObserver; +import jsjava.awt.image.ImageProducer; +import jsjava.awt.image.VolatileImage; +import jsjava.awt.peer.ContainerPeer; +import jsjava.awt.peer.LightweightPeer; +import jsjavax.swing.AbstractButton; +import jsjavax.swing.JComponent; +import jsjavax.swing.JRootPane; +import jsjavax.swing.plaf.ComponentUI; +import jssun.awt.CausedFocusEvent.Cause; +import swingjs.JSToolkit; +import swingjs.api.DOMNode; +import swingjs.api.JQueryObject; + +/** + * The JSComponentUI subclasses are where all the detailed HTML5 implementation is + * carried out. These subclasses mirror the subclasses found in the actual javax.swing.plaf + * but have an important difference in that that effectively act as both the UI (a single + * implementation for a given AppContext in Swing) and a peer (one implementation per component). + * + * So here we store both the constants for the HTML5 "LookAndFeel", but also + * HTML5 objects that really are on the page. + * + * Essentially, at least for now, we are not implementing the HTML5LookAndFeel as such. We'll see how that goes. + * + * + * + * @author Bob Hanson + * + */ +public abstract class JSComponentUI extends ComponentUI implements JSEventHandler { + + /** + * provides a unique id for any component; set on instantiation + */ + protected static int incr; + + + /** + * a unique id + */ + protected String id; + + /** + * the associated JComponent; for which this is c.ui + * + */ + protected JComponent c; + + + /** + * the outermost div holding a component -- left, top, and for a container width and height + */ + protected DOMNode outerNode; + + /** + * the main object for the component, possibly containing others, such as radio button with its label + */ + protected DOMNode domNode; + + /** + * a component or subcomponent that can be enabled/disabled + */ + protected DOMNode enableNode; + + /** + * the part of a component that can hold text + */ + protected DOMNode textNode; + + /** + * the subcomponent with the value field + */ + protected DOMNode valueNode; + + /** + * a component that is being scrolled by a JScrollPane + */ + protected DOMNode scrollNode; + + + /** + * a component that is focusable + */ + protected DOMNode focusNode; + + + /** + * DOM components pre-defined (JScrollPane) + * + */ + protected Component[] components; + + /** + * a numerical reference for an ID + */ + protected int num; + + /** + * not implemented/needed currently. Java handles this nicely + * + */ + protected boolean isTainted = true; + + /** + * left and top coordinates + */ + protected int x, y; + + /** + * preferred dimension set by user + * + */ + protected Dimension preferredSize; + + + /** + * panels + * + */ + protected boolean isContainer; + + /** + * linked nodes of this class + * + */ + protected JSComponentUI parent; + + + String currentText; + + + /** + * the scroller for a text area + */ + protected JSScrollPaneUI scrollerNode; + + + /** + * uiClassID for this component + */ + protected String classID; + + + private DOMNode document, body; + + + protected boolean needPreferred; + + + + public JSComponentUI() { + setDoc(); + } + + protected void setDoc() { + /** + * @j2sNative + * + * this.document = document; + * this.body = document.body; + */ + {} + } + + protected abstract void installJSUI(); + protected abstract void uninstallJSUI(); + + public void installUI(JComponent c) { + // already done installJSUI(); + } + + public void uninstallUI(JComponent c) { + uninstallJSUI(); + } + + protected JQueryObject $(DOMNode node) { + return JSToolkit.getJQuery().$(node); + } + + /** + * mark this component as in need of update; + * maybe not necessary, though. It comes after the value callback + */ + public void setTainted() { + isTainted = true; + } + + public abstract DOMNode getDOMObject(); + + public JSComponentUI set(JComponent target) { + c = target; + newID(); + if (needPreferred) + getPreferredSize(c); + installJSUI(); // need to do this immediately, not later + return this; + } + + protected void newID() { + classID = c.getUIClassID(); + if (id == null) { + num = ++incr; + id = c.getHTMLName(classID) + "_" + num; + } + } + + protected DOMNode setCssFont(DOMNode obj, Font font) { + if (font != null) { + int istyle = font.getStyle(); + String name = font.getFamily(); + if (name == "Dialog") + name = "Arial"; + DOMNode.setStyles(obj, "font-family", name, "font-size", + font.getSize() + "px", "font-style", + ((istyle & Font.ITALIC) == 0 ? "normal" : "italic"), "font-weight", + ((istyle & Font.BOLD) == 0 ? "normal" : "bold")); + } + if (c.isBackgroundSet()) + setBackground(c.getBackground()); + setForeground(c.getForeground()); + return obj; + } + + protected DOMNode createDOMObject(String key, String id, String... attr) { + DOMNode obj = DOMNode.createElement(key, id); + for (int i = 0; i < attr.length;) + DOMNode.setAttr(obj, attr[i++], attr[i++]); + if (!c.isEnabled()) + setEnabled(false); + return obj; + } + + /** + * JSmolCore.js will look for data-UI attribute and, if found, reroute directly here + * @param node + */ + protected void bindMouse(DOMNode node) { + DOMNode.setAttr(node, "data-UI", this); + } + + /** + * called by JmolCore.js + * @return true if handled + */ + public boolean handleJSEvent(Object target, int eventType, Object jQueryEvent) { + //System.out.println(id + " handling event " + eventType + jQueryEvent); + return false; + } + + protected DOMNode wrap(String type, String id, DOMNode... elements) { + return append(createDOMObject(type, id + type), elements); + } + + protected DOMNode append(DOMNode obj, DOMNode[] elements) { + for (int i = 0; i < elements.length; i++) { + obj.appendChild(elements[i]); + } + return obj; + } + + protected void debugDump(DOMNode d) { + System.out.println(DOMNode.getAttr(d, "outerHTML")); + } + + protected static void vCenter(DOMNode obj, int offset) { + DOMNode.setStyles(obj, + "top", "50%", + "transform","translateY(" + offset + "%)"); + } + + /** + * overloaded to allow panel and radiobutton to handle slightly differently + * + * @param obj + * @param addCSS + * @return + */ + protected Dimension setHTMLSize(DOMNode obj, boolean addCSS) { + return setHTMLSize1(obj, addCSS, true); + } + + /** + * also called by JSRadioButtonUI so that it can calculate + * subset dimensions + * + * @param node + * @param addCSS + * @param usePreferred + * @return + */ + @SuppressWarnings("unused") + protected Dimension setHTMLSize1(DOMNode node, boolean addCSS, boolean usePreferred) { + if (node == null) + return null; + int h, w; + String w0 = null, h0 = null; + DOMNode parentNode = null; + + if (scrollerNode != null) { + w = scrollerNode.c.getWidth(); + h = scrollerNode.c.getHeight(); + } else if (usePreferred && preferredSize != null) { + // user has set preferred size + w = preferredSize.width; + h = preferredSize.height; + } else { + // determine the natural size of this object + // save the parent node -- we will need to reset that. + parentNode = DOMNode.remove(node); + + // remove position, width, and height, because those are what we are + // setting here + /** + * @j2sNative + * + * w0 = node.style.width; + * h0 = node.style.height; + */ + {} + DOMNode.setStyles(node, "position", null, "width", null, "height", null); + DOMNode div; + if (DOMNode.getAttr(node, "tagName") == "DIV") + div = node; + else + div = wrap("div", id + "_temp", node); + DOMNode.setStyles(div, "position", "absolute"); + + // process of discovering width and height is facilitated using jQuery + // and appending to document.body. + + body.appendChild(div); + + //System.out.println(DOMNode.getAttr(node, "outerHTML")); + w = (int) Math.ceil($(div).width() + 0.5); + h = (int) Math.ceil($(div).height() + 0.5); + body.removeChild(div); + } + + Dimension size = getCSSDimension(w, h); + if (addCSS) { + DOMNode.setStyles(node, "position", "absolute"); + DOMNode.setSize(node, size.width, size.height); + } else { + DOMNode.setStyles(node, "position", null); + // check to reset width/height after getPreferredSize + if (w0 != null) + DOMNode.setStyles(node, "width", w0, "height", h0); + } + if (parentNode != null) + parentNode.appendChild(node); + //System.out.println("JSComponentUI " + id + " resized to " + w + "x" + h + " parent=" + DOMNode.getAttr(parentNode,"id")); + return size; + } + + /** + * can be overloaded to allow some special adjustments + * + * @param w + * @param h + * @return + */ + protected Dimension getCSSDimension(int w, int h) { + return new Dimension(w, h); + } + + /** + * creates the DOM node and inserts it into the tree at the correct place, + * iterating through all children if this is a container + * + * @return + * + */ + protected DOMNode setHTMLElement() { + if (!isTainted) + return outerNode; + + // check for root pane -- not included in DOM + JRootPane root = (isContainer ? c.getRootPane() : null); + if (c == root) { + isTainted = false; + return outerNode; + } + + domNode = getDOMObject(); + + // divObj will need recreating if a propertyChange event has occurred + // check for content pane -- needs to be added to the HTML5 content layer + // div + + // needs some work for changes after applet creation + + if (outerNode == null) { + outerNode = wrap("div", id, domNode); + if (root != null && root.getContentPane() == c) + swingjs.JSToolkit.getHTML5Applet(c)._getContentLayer() + .appendChild(outerNode); + } + + // set position + + DOMNode.setStyles(outerNode, "position", "absolute", "left", (x = c.getX()) + + "px", "top", (y = c.getY()) + "px"); + + if (isContainer) { + + // set width from component + + System.out.println("JSComponentUI container " + id + " " + c.getBounds()); + DOMNode.setSize(outerNode, c.getWidth(), c.getHeight()); + + // add all children + Component[] children = (components == null ? c.getComponents() + : components); + for (int i = children.length; --i >= 0;) { + JSComponentUI ui = JSToolkit.getUI(children[i], false); + if (ui == null) { + // Box.Filler has no ui. + continue; + } + if (ui.outerNode == null) + ui.setHTMLElement(); + if (ui.outerNode == null) { + System.out.println("JSCUI could not add " + ui.c.getName() + " to " + + c.getName()); + } else { + outerNode.appendChild(ui.outerNode); + } + ui.parent = this; + } + } + + // mark as not tainted + // debugDump(divObj); + isTainted = false; + return outerNode; + } + + /** + * c ignored because JSComponentUI is one per component + */ + public Dimension getPreferredSize(JComponent c) { + //System.out.println("getPreferredSize for " + id + " " + c.getName()); + Dimension d = setHTMLSize(getDOMObject(), false); + //System.out.println("JSComponentUI " + id + " getting preferred size as " + d); + return d; + } + + public void paint(Graphics g, JComponent c) { + // Note that for now, button graphics + // are BEHIND the button. We will need to paint onto the + // glass pane for this to work, and then also manage + // mouse clicks and key clicks with that in mind. + if (c.isOpaque()) { + g.setColor(c.getBackground()); + g.fillRect(0, 0, c.getWidth(), c.getHeight()); + } + } + + public void update(Graphics g, JComponent c) { + // called from JComponent.paintComponent + boolean testing = false;//true; + if (testing) { + g.setColor(Color.red); + g.drawRect(0, 0, c.getWidth(), c.getHeight()); + System.out.println("drawing " + c.getWidth() + " " + c.getHeight()); + } + setHTMLElement(); + paint(g, c); + } + + public Dimension getMinimumSize(JComponent c) { + return getPreferredSize(c); + } + + public Dimension getMaximumSize(JComponent c) { + return null;// getPreferredSize(c); + } + + /** + * Returns true if the specified x,y location is + * contained within the look and feel's defined shape of the specified + * component. x and y are defined to be relative + * to the coordinate system of the specified component. Although + * a component's bounds is constrained to a rectangle, + * this method provides the means for defining a non-rectangular + * shape within those bounds for the purpose of hit detection. + * + * @param c the component where the x,y location is being queried; + * this argument is often ignored, + * but might be used if the UI object is stateless + * and shared by multiple components + * @param x the x coordinate of the point + * @param y the y coordinate of the point + * + * @see jsjavax.swing.JComponent#contains + * @see jsjava.awt.Component#contains + */ + public boolean contains(JComponent c, int x, int y) { + return c.inside(x, y); + } + + /** + * Returns an instance of the UI delegate for the specified component. + * Each subclass must provide its own static createUI + * method that returns an instance of that UI delegate subclass. + * If the UI delegate subclass is stateless, it may return an instance + * that is shared by multiple components. If the UI delegate is + * stateful, then it should return a new instance per component. + * The default implementation of this method throws an error, as it + * should never be invoked. + */ + public static ComponentUI createUI(JComponent c) { + // SwingJS so, actually, we don't do this. This class is NOT stateless. + // Instead, what we do is to create a unique instance + // right in UIManager. The sequence is: + // JRadioButton.updateUI() + // --> jsjavax.swing.UIManager.getUI(this) + // --> jsjavax.swing.UIManager.getDefaults().getUI(target) + // --> JSToolkit.getComponentUI(target) + // --> creates an instance of JRadioButtonUI and returns + // that instance as JRadioButton.ui, which is NOT static. + // +// throw new Error("ComponentUI.createUI not implemented."); + return null; + } + + /** + * Returns the baseline. The baseline is measured from the top of + * the component. This method is primarily meant for + * LayoutManagers to align components along their + * baseline. A return value less than 0 indicates this component + * does not have a reasonable baseline and that + * LayoutManagers should not align this component on + * its baseline. + *

+ * This method returns -1. Subclasses that have a meaningful baseline + * should override appropriately. + * + * @param c JComponent baseline is being requested for + * @param width the width to get the baseline for + * @param height the height to get the baseline for + * @throws NullPointerException if c is null + * @throws IllegalArgumentException if width or height is < 0 + * @return baseline or a value < 0 indicating there is no reasonable + * baseline + * @see jsjavax.swing.JComponent#getBaseline(int,int) + * @since 1.6 + */ + public int getBaseline(JComponent c, int width, int height) { + if (c == null) { + throw new NullPointerException("Component must be non-null"); + } + if (width < 0 || height < 0) { + throw new IllegalArgumentException( + "Width and height must be >= 0"); + } + return -1; + } + + /** + * Returns an enum indicating how the baseline of he component + * changes as the size changes. This method is primarily meant for + * layout managers and GUI builders. + *

+ * This method returns BaselineResizeBehavior.OTHER. + * Subclasses that support a baseline should override appropriately. + * + * @param c JComponent to return baseline resize behavior for + * @return an enum indicating how the baseline changes as the component + * size changes + * @throws NullPointerException if c is null + * @see jsjavax.swing.JComponent#getBaseline(int, int) + * @since 1.6 + */ + public Component.BaselineResizeBehavior getBaselineResizeBehavior( + JComponent c) { + if (c == null) { + throw new NullPointerException("Component must be non-null"); + } + return Component.BaselineResizeBehavior.OTHER; + } + + /** + * overridden in JSPasswordFieldUI + * @return texat + */ + public String getJSTextValue() { + return (String) DOMNode.getAttr(domNode, valueNode == null ? "innerHTML" : "value"); + } + + public void notifyPropertyChanged(String prop) { + DOMNode obj = null; + String val = null; + if (prop == "text") { + val = ((AbstractButton) c).getText(); + if (val.equals(currentText)) // we set it here, then fired the property change + return; + currentText = val; + if (textNode != null) { + prop = "innerHTML"; + obj = textNode; + } else if (valueNode != null) { + prop = "value"; + obj = valueNode; + } + } else if (prop == "preferredSize") { + preferredSize = c.getPreferredSize(); // may be null + getPreferredSize(); + return; + } + if (obj == null) { + System.out.println("JSComponentUI: unrecognized prop: " + prop); + } else { + System.out.println("JSComponentUI: setting " + id + " " + prop);// + " " + val); + setProp(obj, prop, val); + } + } + + protected DOMNode setProp(DOMNode obj, String prop, String val) { + return DOMNode.setAttr(obj, prop, val); + } + + @Override + public boolean isObscured() { + JSToolkit.notImplemented(""); + return false; + } + + @Override + public boolean canDetermineObscurity() { + JSToolkit.notImplemented(""); + return false; + } + + @Override + public void setVisible(boolean b) { + DOMNode.setStyles(outerNode, "display", b ? "block" : "none"); + } + + @Override + public void setEnabled(boolean b) { + if (enableNode != null) + DOMNode.setAttr(enableNode, "disabled", (b ? null : "TRUE")); + } + + @Override + public void paint(Graphics g) { + // nothing to do here + } + + @Override + public void repaint(long tm, int x, int y, int width, int height) { + // nothing to do here + } + + @Override + public void print(Graphics g) { + JSToolkit.notImplemented(""); + } + + @Override + public void setBounds(int x, int y, int width, int height, int op) { + switch (op) { + case SET_SIZE: + case SET_BOUNDS: + case SET_CLIENT_SIZE: + if (scrollerNode != null) { + width = Math.min(width, scrollerNode.c.getWidth()); + height = Math.min(height, scrollerNode.c.getHeight()); + } + System.out.println(id + " setBounds " + x + " " + y + " " + width + " " + height + " op=" + op); + if (domNode != null) + DOMNode.setSize(domNode, width, height); + break; + } + } + + @Override + public void handleEvent(AWTEvent e) { + JSToolkit.notImplemented(""); + + } + + @Override + public void coalescePaintEvent(PaintEvent e) { + JSToolkit.notImplemented(""); + + } + + /** + * Coordinates relative to the document + * + */ + @Override + public Point getLocationOnScreen() { + Insets offset = (Insets) $(outerNode).offset(); + return new Point(offset.left, offset.top); + } + + @Override + public Dimension getPreferredSize() { + return getPreferredSize(c); + } + + @Override + public Dimension getMinimumSize() { + JSToolkit.notImplemented(""); + return getPreferredSize(c); + } + + @Override + public ColorModel getColorModel() { + return Toolkit.getDefaultToolkit().getColorModel(); + } + + @Override + public Toolkit getToolkit() { + return Toolkit.getDefaultToolkit(); + } + + @Override + public Graphics getGraphics() { + // n/a -- called from java.awt.Component when NOT a LightweightPeer. + return null; + } + + @Override + public FontMetrics getFontMetrics(Font font) { + return c.getFontMetrics(font); + } + + @Override + public void dispose() { + JSToolkit.notImplemented(""); + } + + @Override + public void setForeground(Color color) { + if (domNode != null) + DOMNode.setStyles(domNode, "color", JSToolkit.getCSSColor(color == null ? Color.black : color)); + } + + @Override + public void setBackground(Color color) { + if (domNode != null) + DOMNode.setStyles(domNode, "background-color", JSToolkit.getCSSColor(color == null ? Color.white : color)); + } + + @Override + public void setFont(Font f) { + if (domNode != null) + setCssFont(domNode, f); + } + + @Override + public void updateCursorImmediately() { + JSToolkit.notImplemented(""); + } + + @Override + public boolean requestFocus(Component lightweightChild, boolean temporary, + boolean focusedWindowChangeAllowed, long time, Cause cause) { + if (focusNode == null) + return false; + $(focusNode).focus(); + if (textNode != null) + $(textNode).select(); + return true; + } + + @Override + public boolean isFocusable() { + return (focusNode != null); + } + + @Override + public Image createImage(ImageProducer producer) { + JSToolkit.notImplemented(""); + return null; + } + + @Override + public Image createImage(int width, int height) { + JSToolkit.notImplemented(""); + return null; + } + + @Override + public VolatileImage createVolatileImage(int width, int height) { + JSToolkit.notImplemented(""); + return null; + } + + @Override + public boolean prepareImage(Image img, int w, int h, ImageObserver o) { + JSToolkit.notImplemented(""); + return false; + } + + @Override + public int checkImage(Image img, int w, int h, ImageObserver o) { + JSToolkit.notImplemented(""); + return 0; + } + + @Override + public GraphicsConfiguration getGraphicsConfiguration() { + JSToolkit.notImplemented(""); + return null; + } + + @Override + public boolean handlesWheelScrolling() { + JSToolkit.notImplemented(""); + return false; + } + + @Override + public Image getBackBuffer() { + JSToolkit.notImplemented(""); + return null; + } + + @Override + public void destroyBuffers() { + JSToolkit.notImplemented(""); + + } + + @Override + public void reparent(ContainerPeer newContainer) { + JSToolkit.notImplemented(""); + + } + + @Override + public boolean isReparentSupported() { + JSToolkit.notImplemented(""); + return false; + } + + @Override + public void layout() { + JSToolkit.notImplemented(""); + + } + + @Override + public Rectangle getBounds() { + JSToolkit.notImplemented(""); + return null; + } + + public boolean hasFocus() { + return focusNode != null && focusNode == DOMNode.getAttr(document, "activeElement"); + } + + public void notifyFocus(boolean focusGained) { + Toolkit.getEventQueue().postEvent(new FocusEvent(c, focusGained ? FocusEvent.FOCUS_GAINED : FocusEvent.FOCUS_LOST)); + } +}