/* * Copyright (c) 2003-onwards Shaven Puppy Ltd * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'Shaven Puppy' nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.puppygames.applet; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; import net.puppygames.applet.effects.Emitter; import net.puppygames.applet.effects.EmitterFeature; import net.puppygames.applet.effects.SFX; import org.lwjgl.input.Mouse; import org.lwjgl.util.Color; import org.lwjgl.util.Dimension; import org.lwjgl.util.Point; import org.lwjgl.util.ReadableColor; import org.lwjgl.util.ReadablePoint; import org.lwjgl.util.ReadableRectangle; import org.lwjgl.util.Rectangle; import org.w3c.dom.Element; import com.shavenpuppy.jglib.XMLResourceWriter; import com.shavenpuppy.jglib.opengl.ColorUtil; import com.shavenpuppy.jglib.opengl.GLFont; import com.shavenpuppy.jglib.opengl.GLRenderable; import com.shavenpuppy.jglib.opengl.GLStyledText; import com.shavenpuppy.jglib.opengl.GLStyledText.HorizontalAlignment; import com.shavenpuppy.jglib.opengl.GLStyledText.VerticalAlignment; import com.shavenpuppy.jglib.resources.Background; import com.shavenpuppy.jglib.resources.Data; import com.shavenpuppy.jglib.resources.Feature; import com.shavenpuppy.jglib.resources.MappedColor; import com.shavenpuppy.jglib.resources.RectangleParser; import com.shavenpuppy.jglib.sprites.Appearance; import com.shavenpuppy.jglib.sprites.Sprite; import com.shavenpuppy.jglib.sprites.SpriteImage; import com.shavenpuppy.jglib.util.FPMath; import com.shavenpuppy.jglib.util.XMLUtil; import static org.lwjgl.opengl.GL11.*; /** * $Id: Area.java,v 1.49 2010/11/03 16:21:37 foo Exp $ * An Area in a Screen. * @author $Author: foo $ * @version $Revision: 1.49 $ */ public class Area extends Feature implements Tickable, Bounded { public static final long serialVersionUID = 1L; /* * Static data */ private static final Color TEMPCOLOR = new Color(); /** Id */ @Data private String id; /** Group or groups (separated by commas) */ @Data private String group; /** Sprite image when mouse is not over the area */ @Data private String mouseOff; /** Sprite image when mouse is over the area */ @Data private String mouseOn; /** Sprite image when disabled */ @Data private String disabled; /** Hitboxes */ private boolean useBounds; private List<ReadableRectangle> hitBoxes; /** Position */ private Point position; /** Optional size, if no images specified */ private Dimension size; /** Optional sprite offset */ private Point offset; /** Whether to click */ private boolean noClick; /** Debug */ private boolean debug; /** Layer */ private int layer; /** Visibility */ private boolean visible = true; /** Font */ private String font; /** Text */ @Data private String text; /** Text alignement */ @Data private String halign, valign; /** Colour */ private MappedColor color, disabledColor, mouseOnColor; /** Top colour */ private MappedColor topColor, disabledTopColor, mouseOnTopColor; /** Bottom colour */ private MappedColor bottomColor, disabledBottomColor, mouseOnBottomColor; /** Left focus */ @Data private String leftFocus; /** Right focus */ @Data private String rightFocus; /** Up focus */ @Data private String upFocus; /** Down focus */ @Data private String downFocus; /** Next focus */ @Data private String nextFocus; /** Previous focus */ @Data private String prevFocus; /** Default focus */ private boolean defaultFocus; /** Mirrored */ private boolean mirrored; /** Flipped */ private boolean flipped; /** Background */ @Data private String background, disabledBackground, mouseOnBackground; /** Background layer */ private int bglayer; /** Text layer */ private int textLayer; /** Leading, for text */ private int leading; /** Flags for monkeying */ private boolean hasSize, hasPosition; /** Whether to write text as sub-tag in XML output */ private boolean textAsSubTag; /** Text offset */ private Point textOffset; /** Emitter */ @Data private String emitter; /** Emitter offset */ private Point emitterOffset; /** Linked area */ @Data private String link; /** Scale of Sprite image */ private float scale = 0.0f; /** Text shadow colour */ private MappedColor textShadowColor; /** Text shadow offset */ private Point textShadowOffset; /** Text bounds */ private Rectangle textBounds; /** Initial alpha */ private int alpha, textAlpha; /** Master */ @Data private String master; /** Anchors */ private ArrayList<Anchor> anchors; /** Hold click down */ private boolean holdClick; /** All caps */ private boolean allCaps; /** Grab on click */ private boolean grab; /* * Transient data */ private transient Appearance mouseOnResource, mouseOffResource, disabledResource; private transient int state; private static final int STATE_MOUSEOFF = 0; private static final int STATE_MOUSEON = 1; private static final int STATE_DISABLED = 2; private transient Sprite sprite; private transient Rectangle bounds; private transient Screen screen; private transient boolean ignoreMouse; private transient boolean enabled; private transient GLStyledText textArea; private transient String[] groups; private transient Background backgroundResource, disabledBackgroundResource, mouseOnBackgroundResource; private transient TickableObject backgroundObject, textObject; private transient GLFont fontResource; private transient boolean selectDown, armed, initing, waitForMouse; private transient EmitterFeature emitterFeature; private transient Appearance currentAppearance; private transient Background.Instance backgroundInstance; private transient Emitter emitInstance; private transient List<Area> slaves; private transient Area masterArea; /** * C'tor */ public Area() { super(); setAutoCreated(); } /** * C'tor * @param name */ public Area(String name) { super(name); setAutoCreated(); } /** * @return the bounds (never null) */ @Override public ReadableRectangle getBounds() { if (bounds == null) { if (sprite != null) { SpriteImage image = sprite.getImage(); if (image != null) { bounds = new Rectangle((int) sprite.getX(), (int) sprite.getY(), image.getWidth(), image.getHeight()); } } if (bounds == null) { bounds = new Rectangle(0, 0, 0, 0); } } return bounds; } /** * @return the layer */ public int getLayer() { return layer; } /* (non-Javadoc) * @see com.shavenpuppy.jglib.resources.Feature#load(org.w3c.dom.Element, com.shavenpuppy.jglib.Resource.Loader) */ @Override public void load(Element element, Loader loader) throws Exception { super.load(element, loader); if (size != null) { hasSize = true; } if (position != null) { hasPosition = true; } if (text == null && XMLUtil.hasChild(element, "text")) { text = XMLUtil.getText(XMLUtil.getChild(element, "text"), "<missing text>"); textAsSubTag = true; } if (!XMLUtil.hasAttribute(element, "alpha")) { alpha = 255; } if (!XMLUtil.hasAttribute(element, "textAlpha")) { textAlpha = 255; } Element hitboxesElement = XMLUtil.getChild(element, "hitboxes"); if (hitboxesElement != null) { useBounds = XMLUtil.getBoolean(hitboxesElement, "useBounds", false); List<Element> hitBoxesChildren = XMLUtil.getChildren(hitboxesElement, "hitbox"); hitBoxes = new ArrayList<ReadableRectangle>(hitBoxesChildren.size() + 1); for (Element child : hitBoxesChildren) { Rectangle r = RectangleParser.parse(XMLUtil.getText(child, "0,0,0,0")); hitBoxes.add(r); } } // Anchors List<Element> anchorElements = XMLUtil.getChildren(element, "anchor"); if (anchorElements.size() > 0) { anchors = new ArrayList<Anchor>(anchorElements.size()); for (Element anchorChild : anchorElements) { Anchor anchor = (Anchor) loader.load(anchorChild); anchors.add(anchor); } } if (allCaps && text != null) { text = text.toUpperCase(); } } /** * Called immediately when screen is opened */ public void init() { onResized(); if (sprite != null && mouseOffResource != null) { sprite.setAppearance(mouseOffResource); sprite.rewind(); if (offset != null) { sprite.setOffset(offset.getX(), offset.getY()); } updateScale(); } maybeCreateTextObject(); if (emitterFeature != null) { initEmitter(); } doSetVisible(visible); initing = true; setAppearanceForState(); syncSlaves(); waitForMouse = true; initing = false; } public void waitForMouse() { waitForMouse = true; } public void onResized() { // Apply anchors unless screen is "centred" if (anchors != null) { for (Anchor anchor : anchors) { anchor.apply(this); } } else if (position != null) { boolean centreX = screen.isCentred() || screen.isCentredX(); boolean centreY = screen.isCentred() || screen.isCentredY(); if (centreX || centreY) { ReadableRectangle bounds = getBounds(); int newX = centreX ? (Game.getWidth() - Game.getScale()) / 2 + position.getX() : position.getX(); int newY = centreY ? (Game.getHeight() - Game.getScale()) / 2 + position.getY() : position.getY(); boolean hasPositionOld = hasPosition; // Hack to workaround position being changed in setBounds hasPosition = false; setBounds(newX, newY, bounds.getWidth(), bounds.getHeight()); hasPosition = hasPositionOld; } } updateTextColors(); } /** * Maybe create text object? */ private void maybeCreateTextObject() { if (textArea != null && getScreen() != null) { if (textObject != null) { textObject.remove(); textObject = null; } textObject = new TickableObject((text.length() + 32) * 4) { @Override protected void render() { if (textShadowColor != null && textShadowOffset != null) { glRender(new GLRenderable() { @Override public void render() { glPushMatrix(); glTranslatef(textShadowOffset.getX(), textShadowOffset.getY(), 0.0f); } }); ColorUtil.setAlpha(textShadowColor, alpha, TEMPCOLOR); textArea.setColor(TEMPCOLOR); try { textArea.render(this); } catch (Exception e) { System.out.println("Failed to render "+Area.this+" due to "+e); e.printStackTrace(); } updateTextColors(); glRender(new GLRenderable() { @Override public void render() { glPopMatrix(); } }); textArea.setColor(ReadableColor.WHITE); } textArea.render(this); } }; textObject.setLayer(textLayer); textObject.setVisible(visible); textObject.spawn(screen); } } /** * Create an emitter */ private void initEmitter() { emitInstance = emitterFeature.spawn(getScreen()); int ox, oy; if (emitterOffset != null) { ox = emitterOffset.getX(); oy = emitterOffset.getY(); } else { ox = 0; oy = 0; } emitInstance.setLocation(position.getX() + ox, position.getY() + oy); } @Override protected void doCreate() { super.doCreate(); if (group != null) { StringTokenizer st = new StringTokenizer(group, ",", false); groups = new String[st.countTokens()]; for (int count = 0 ; st.hasMoreTokens(); count ++) { groups[count] = st.nextToken().trim(); } } if (mouseOffResource != null) { SpriteImage img; if (mouseOffResource instanceof SpriteImage) { img = (SpriteImage) mouseOffResource; if (position != null && img != null) { bounds = new Rectangle(position.getX() - img.getHotspotX(), position.getY() - img.getHotspotY(), img.getWidth(), img.getHeight()); if (!hasSize) { size = new Dimension(img.getWidth(), img.getHeight()); } } } } if (size == null && mouseOff != null) { //throw new Exception("Can't create area "+this+": "+mouseOff+" not found."); } // If both size and position are explicit, override any bounds from the mouseOff image if (size != null && position != null) { bounds = new Rectangle(position, size); } else if (position != null) { bounds = new Rectangle(position, new Dimension(0, 0)); } else if (size != null) { bounds = new Rectangle(new Point(0, 0), size); } else { bounds = new Rectangle(); } if (text != null) { createTextArea(); } enabled = true; } @Override protected void doDestroy() { super.doDestroy(); cleanup(); } private void maybeCreateSprite() { if (mouseOffResource != null) { if (sprite != null) { sprite.setVisible(visible); sprite.setAlpha(alpha); return; } sprite = screen.allocateSprite(this); sprite.setAlpha(alpha); sprite.setLayer(layer); sprite.setLocation(bounds.getX(), bounds.getY()); sprite.setMirrored(mirrored); sprite.setFlipped(flipped); sprite.setVisible(visible); if (getOffset() != null) { sprite.setOffset(getOffset().getX(), getOffset().getY()); } updateScale(); } else { if (sprite != null) { if (sprite.isAllocated()) { sprite.deallocate(); } sprite = null; } } } private void updateScale() { if (scale != 1 && scale != 0 && sprite != null) { sprite.setScale(FPMath.fpValue(scale)); } } /** * Init * @param newScreen */ @Override public void spawn(Screen newScreen) { this.screen = newScreen; newScreen.addTickable(this); maybeCreateSprite(); setBackground(backgroundResource); if (master != null) { masterArea = newScreen.getArea(master); if (masterArea == null) { System.out.println("Warning: Area "+id+" references master "+master+", which does not exist on screen "+newScreen); } else { masterArea.addSlave(this); } } syncSlaves(); } private void addSlave(Area slave) { if (slaves == null) { slaves = new ArrayList<Area>(); } if (!slaves.contains(slave)) { slaves.add(slave); } syncSlaves(); } /** * Synchronizes mouse over, visible, and enabled state down to each of the slaves */ private void syncSlaves() { syncVisibility(); syncEnabled(); syncStates(); } private GLFont selectFont() { GLFont ret = fontResource; if (ret == null) { ret = Res.getTinyFont(); } return ret; } /** * Cleanup */ public void cleanup() { if (sprite != null) { if (sprite.isAllocated()) { sprite.deallocate(); } sprite = null; } if (backgroundObject != null) { backgroundObject.remove(); backgroundObject = null; } if (textObject != null) { textObject.remove(); textObject = null; } if (emitInstance != null) { emitInstance.remove(); emitInstance = null; } currentAppearance = null; slaves = null; } @Override public void update() { // No need to do anything, it's all handled in tick() } private boolean isInHitZone(int x, int y) { // Check slaves if (slaves != null) { for (int i = 0; i < slaves.size(); i ++) { Area slave = slaves.get(i); if (slave.isInHitZone(x, y)) { return true; } } } if (hitBoxes != null) { for (int i = 0; i < hitBoxes.size(); i ++) { Rectangle rect = (Rectangle) hitBoxes.get(i); if (rect.contains(x, y)) { return true; } } } else { if (getBounds() != null) { return bounds.contains(x, y); } } return false; } @Override public void tick() { List<MouseEvent> mouseEvents = Game.getMouseEvents(); int n = mouseEvents.size(); if (n == 0) { doTick(null); } else { for (int i = 0; i < n; i ++) { MouseEvent event = mouseEvents.get(i); doTick(event); } } } private void doTick(MouseEvent event) { if (!screen.isOpen()) { return; } if (screen.getGrabbed() != null && screen.getGrabbed() != this) { return; } if (masterArea != null) { return; } boolean mouseDown = Mouse.isButtonDown(0); if (waitForMouse) { if (mouseDown) { if (!enabled) { setState(STATE_DISABLED); } else { setState(STATE_MOUSEOFF); } return; } else { waitForMouse = false; } } armed = false; if (isVisible()) { if (event != null && event.getButton() == 0) { processMouseChange(event.isButtonDown()); } else { processMouseChange(selectDown); } } } /** * Helper function to process mouse events. * @param mouseDown * @see #tick() */ private void processMouseChange(boolean mouseDown) { boolean clicked = false; armed = isInHitZone(screen.getMouseX(), screen.getMouseY()); selectDown = mouseDown && armed; if (selectDown && armed) { clicked = true; if (grab) { screen.setGrabbed(this); } } else if (grab && !selectDown) { screen.setGrabbed(null); } if (!enabled) { setState(STATE_DISABLED); } else if (screen.isEnabled()) { if (armed) { setState(STATE_MOUSEON); } else { setState(STATE_MOUSEOFF); } } else { setState(STATE_MOUSEOFF); } if (selectDown && visible && armed && !noClick && !ignoreMouse && enabled && screen.isEnabled() && clicked) { if (!holdClick) { SFX.buttonClick(); ignoreMouse = true; } doClick(); } else if (!selectDown) { ignoreMouse = false; } } public void doClick() { if (master != null) { masterArea.doClick(); } else { screen.onClicked(id); } } private void setBackground(Background newBG) { if (newBG != null) { backgroundInstance = newBG.spawn(); backgroundInstance.setBounds(bounds); backgroundInstance.setAlpha(alpha); createBackgroundObject(); } else { if (backgroundObject != null) { backgroundObject.remove(); backgroundObject = null; } backgroundInstance = null; } } private void createBackgroundObject() { if (backgroundObject != null) { backgroundObject.remove(); backgroundObject = null; } backgroundObject = new TickableObject() { @Override protected void render() { backgroundInstance.render(this); } }; backgroundObject.spawn(screen); backgroundObject.setLayer(bglayer); backgroundObject.setVisible(visible); } public void setMouseOnAppearance(Appearance newMouseOnResource) { initing = true; mouseOnResource = newMouseOnResource; currentAppearance = null; setAppearanceForState(); initing = false; } public void setMouseOffAppearance(Appearance newMouseOffResource) { initing = true; mouseOffResource = newMouseOffResource; currentAppearance = null; maybeCreateSprite(); setAppearanceForState(); initing = false; } public Appearance getMouseOffAppearance() { return mouseOffResource; } /** * @return the mouseOnResource */ public Appearance getMouseOnAppearance() { return mouseOnResource; } /** * @return the disabledResource */ public Appearance getDisabledAppearance() { return disabledResource; } public void setDisabledAppearance(Appearance newDisabledResource) { initing = true; disabledResource = newDisabledResource; currentAppearance = null; setAppearanceForState(); initing = false; } private void setAppearanceForState() { switch (state) { case STATE_MOUSEOFF: if (mouseOffResource != null) { setAppearance(mouseOffResource); } setBackground(backgroundResource); if (visible && !noClick && !initing) { screen.onHover(id, false); } break; case STATE_MOUSEON: if (mouseOnResource != null) { setAppearance(mouseOnResource); } if (mouseOnBackgroundResource != null) { setBackground(mouseOnBackgroundResource); } else { setBackground(backgroundResource); } if (visible && !noClick && !initing) { SFX.buttonHover(); screen.onHover(id, true); } break; case STATE_DISABLED: if (disabledResource != null) { setAppearance(disabledResource); } if (disabledBackgroundResource != null) { setBackground(disabledBackgroundResource); } else { setBackground(backgroundResource); } break; default: assert false; } updateTextColors(); } private void setState(int newState) { if (state == newState) { return; } state = newState; setAppearanceForState(); syncStates(); } /** * Synchronizes appearance, armed, and selectDown down to slaves */ private void syncStates() { if (slaves == null) { return; } for (int i = 0; i < slaves.size(); i ++) { Area slave = slaves.get(i); slave.armed = armed; slave.selectDown = selectDown; slave.setState(state); } } private void setAppearance(Appearance appearance) { if (sprite == null || currentAppearance == appearance) { return; } sprite.setAnimation(null); sprite.setAppearance(appearance); currentAppearance = appearance; } // /** // * Maybe render something // */ // public void render() { // if (position != null && size != null && debug) { // glDisable(GL_TEXTURE_2D); // glEnable(GL_BLEND); // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); // glColor4f(1.0f, 1.0f, 1.0f, 0.2f); // glBegin(GL_QUADS); // glVertex2i(position.getX(), position.getY()); // glVertex2i(position.getX() + size.getWidth(), position.getY()); // glVertex2i(position.getX() + size.getWidth(), position.getY() + size.getHeight()); // glVertex2i(position.getX(), position.getY() + size.getHeight()); // glEnd(); // } // } /** * Get the area's ID * @return String */ public String getID() { return id; } @Override public String toString() { return "Area["+id+"]"; } /** * Get the area's sprite * @return sprite */ public Sprite getSprite() { return sprite; } /** * @return the Screen the Area is on */ public Screen getScreen() { return screen; } /** * Set visible * @param visible */ public void setVisible(boolean visible) { if (this.visible == visible) { // Do linked area anyway if (link != null) { Area linked = screen.getArea(link); if (linked != null) { linked.setVisible(visible); } else { System.out.println("Linked area '"+link+"' does not exist"); } } return; } doSetVisible(visible); } protected void doSetVisible(boolean visible) { this.visible = visible; if (visible) { // Ignore the mouse until it is released ignoreMouse = true; } if (sprite != null) { sprite.setVisible(visible); // Restart any animation if (visible) { if (isEnabled() || disabledResource == null) { mouseOffResource.toSprite(sprite); currentAppearance = mouseOffResource; } else { disabledResource.toSprite(sprite); currentAppearance = disabledResource; } } else { currentAppearance = null; sprite.rewind(); } } if (backgroundObject != null) { backgroundObject.setVisible(visible); if (sprite != null) { sprite.setActive(visible); } } if (textObject != null) { textObject.setVisible(visible); } if (emitInstance != null) { if (!visible) { emitInstance.remove(); emitInstance = null; } } else if (emitterFeature != null) { initEmitter(); } // Do link, if any if (link != null) { Area linked = screen.getArea(link); if (linked != null) { linked.doSetVisible(visible); } else { System.out.println("Linked area '"+link+"' does not exist"); } } syncVisibility(); if (visible && isFocusable()) { requestFocus(); } } /** * Synchronizes visibility down to slaves */ private void syncVisibility() { if (slaves == null) { return; } for (int i = 0; i < slaves.size(); i ++) { Area slave = slaves.get(i); slave.setVisible(visible); } } /** * Set alpha * @param alpha 0..255 */ public void setAlpha(int alpha) { this.alpha = alpha; if (sprite != null) { sprite.setAlpha(alpha); } if (backgroundInstance != null) { backgroundInstance.setAlpha(alpha); } updateTextColors(); } /** * @param enabled */ public void setEnabled(boolean enabled) { if (this.enabled == enabled) { return; } this.enabled = enabled; setState(enabled ? STATE_MOUSEOFF : STATE_DISABLED); setAppearanceForState(); // if (sprite != null) { // if (enabled) { // setAppearance(mouseOffResource); // } else if (disabledResource != null) { // setAppearance(disabledResource); // } // } if (link != null) { Area linked = screen.getArea(link); if (linked != null) { linked.setEnabled(enabled); } else { System.err.println("Linked area '"+link+"' does not exist"); } } syncEnabled(); } /** * Synchronizes visibility down to slaves */ private void syncEnabled() { if (slaves == null) { return; } for (int i = 0; i < slaves.size(); i ++) { Area slave = slaves.get(i); slave.setEnabled(enabled); } } /** * Is the area enabled? * @return boolean */ public boolean isEnabled() { return enabled; } /** * Is the area visible? * @return boolean */ public boolean isVisible() { return visible; } /* (non-Javadoc) * @see net.puppygames.applet.Tickable#isActive() */ @Override public boolean isActive() { return isCreated(); } /* (non-Javadoc) * @see net.puppygames.applet.Tickable#remove() */ @Override public void remove() { // Ignore } /** * Gets the name of the Area that should receive focus next * @return String or null */ public String getNextFocus() { return nextFocus; } /** * Gets the name of the Area that should receive focus before * @return String or null */ public String getPrevFocus() { return prevFocus; } public String getLeftFocus() { return leftFocus; } public String getRightFocus() { return rightFocus; } public String getUpFocus() { return upFocus; } public String getDownFocus() { return downFocus; } /** * Are we focused? * @return boolean */ public boolean isFocused() { return screen.getFocus() == this && screen.isKeyboardNavigationEnabled() && visible && enabled && !noClick; } /** * @return */ public boolean isDefaultFocus() { return defaultFocus; } /** * Are we focusable? * @return boolean */ public boolean isFocusable() { return visible && enabled && !noClick && masterArea == null; } /** * Is this area in this group? * @param groupToCheck */ public boolean isInGroup(String groupToCheck) { if (groupToCheck == null || groups == null) { return false; } for (int i = 0; i < groups.length; i ++) { if (groupToCheck.equals(groups[i])) { return true; } } return false; } /** * Request focus on this Area */ private void requestFocus() { screen.requestFocus(this); } /** * @return the background instance, if any */ public Background.Instance getBackground() { return backgroundInstance; } /** * Get the current image displayed, if any * @return a SpriteImage, or null */ public SpriteImage getCurrentImage() { return sprite != null ? sprite.getImage() : null; } /** * @return the position */ public ReadablePoint getPosition() { return position; } /** * Set the bounds */ @Override public void setBounds(int x, int y, int w, int h) { bounds.setBounds(x, y, w, h); if (sprite != null) { sprite.setLocation(x, y); } if (hasSize) { size.setSize(w, h); } if (hasPosition) { position.setLocation(x, y); } if (textArea != null) { updateTextBounds(); } if (backgroundInstance != null) { backgroundInstance.setBounds(bounds); } if (emitInstance != null) { int ox, oy; if (emitterOffset != null) { ox = emitterOffset.getX(); oy = emitterOffset.getY(); } else { ox = 0; oy = 0; } emitInstance.setLocation(x + ox, y + oy); } } public void setTextOffset(int x, int y) { if (x == 0 && y == 0) { textOffset = null; } else { textOffset = new Point(x, y); } updateTextBounds(); } public GLFont getFont() { return fontResource; } public String getText() { return text; } /** * @return the textOffset */ public ReadablePoint getTextOffset() { if (textOffset == null) { return new Point(); } else { return textOffset; } } private void updateTextBounds() { if (textArea == null) { return; } Rectangle b = null; if (bounds != null) { b = bounds; } if (textBounds != null) { b = textBounds; if (bounds != null) { b.translate(bounds); } } if (b == null) { return; } int x = b.getX(); int y = b.getY(); int w = b.getWidth(); int h = b.getHeight(); if (textOffset != null) { x += textOffset.getX(); y += textOffset.getY(); } textArea.setBounds(x, y, w, h); } /* (non-Javadoc) * @see com.shavenpuppy.jglib.resources.Feature#shouldWriteAttribute(java.lang.String) */ @Override protected boolean shouldWriteAttribute(String attribute) { if ( "text".equals(attribute) || "textAsSubTag".equals(attribute) || "hasSize".equals(attribute) || "hasPosition".equals(attribute) || "useBounds".equals(attribute) || ( "size".equals(attribute) && !hasSize ) || ( "position".equals(attribute) && !hasPosition ) || ( "alpha".equals(attribute) && alpha == 255 ) ) { return false; } else { return true; } } /* (non-Javadoc) * @see com.shavenpuppy.jglib.resources.Feature#doWriteAttributes(com.shavenpuppy.jglib.XMLResourceWriter) */ @Override protected void doWriteAttributes(XMLResourceWriter writer) throws IOException { if (!textAsSubTag) { writer.writeAttribute("text", text); } } /* (non-Javadoc) * @see com.shavenpuppy.jglib.resources.Feature#doWriteChildren(com.shavenpuppy.jglib.XMLResourceWriter) */ @Override protected void doWriteChildren(XMLResourceWriter writer) throws IOException { if (textAsSubTag) { writer.writeTag("text"); writer.writeText(text); writer.closeTag(); } if (hitBoxes != null) { writer.writeTag("hitboxes"); if (useBounds) { writer.writeAttribute("useBounds", true); } for (ReadableRectangle r : hitBoxes) { writer.writeTag("hitbox"); writer.writeText(String.valueOf(r.getX())); writer.writeText(", "); writer.writeText(String.valueOf(r.getY())); writer.writeText(", "); writer.writeText(String.valueOf(r.getWidth())); writer.writeText(", "); writer.writeText(String.valueOf(r.getHeight())); writer.closeTag(); } writer.closeTag(); } } /** * @param text the text to set */ public void setText(String text) { this.text = allCaps ? text.toUpperCase() : text; if (textArea == null && text != null) { // Create text area createTextArea(); } else if (textArea != null && text == null) { // Remove text area textArea = null; if (textObject != null) { textObject.remove(); textObject = null; } } else if (textArea != null && text != null) { // Update text area // textArea.setText(getFormatString() + text); textArea.setText(text); } } private String getFormatString() { StringBuilder sb = new StringBuilder(32); sb.append("{font:"); sb.append(selectFont().getName()); if (color != null) { sb.append(" color:"); sb.append(color); } else { if (topColor != null) { sb.append(" top:"); sb.append(topColor); } if (bottomColor != null) { sb.append(" bottom:"); sb.append(bottomColor); } } sb.append("}"); return sb.toString(); } private void createTextArea() { textArea = new GLStyledText(); textArea.setText(text); textArea.setLeading(leading); textArea.setAlpha(textAlpha); try { textArea.setVerticalAlignment(valign == null ? GLStyledText.TOP : (VerticalAlignment) VerticalAlignment.decode(valign)); } catch (Exception e) { e.printStackTrace(System.err); } try { textArea.setHorizontalAlignment(halign == null ? GLStyledText.CENTERED : (HorizontalAlignment) HorizontalAlignment.decode(halign)); } catch (Exception e) { e.printStackTrace(System.err); } updateTextColors(); updateTextBounds(); maybeCreateTextObject(); } private void updateTextColors() { if (textArea == null) { return; } ReadableColor currentColor, currentTopColor, currentBottomColor; switch (state) { case STATE_DISABLED: currentColor = disabledColor != null ? disabledColor : color; currentTopColor = disabledTopColor != null ? disabledTopColor : topColor; currentBottomColor = disabledBottomColor != null ? disabledBottomColor : bottomColor; break; case STATE_MOUSEOFF: currentColor = color; currentTopColor = topColor; currentBottomColor = bottomColor; break; case STATE_MOUSEON: currentColor = mouseOnColor != null ? mouseOnColor : color; currentTopColor = mouseOnTopColor != null ? mouseOnTopColor : topColor; currentBottomColor = mouseOnBottomColor != null ? mouseOnBottomColor : bottomColor; break; default: assert false; return; } if (currentTopColor != null & currentBottomColor != null) { textArea.setFactory(new GLStyledText.DefaultStyledTextFactory(currentTopColor, currentBottomColor, selectFont())); } else if (currentColor != null) { textArea.setFactory(new GLStyledText.DefaultStyledTextFactory(currentColor, currentColor, selectFont())); } else { textArea.setFactory(new GLStyledText.DefaultStyledTextFactory(ReadableColor.WHITE, ReadableColor.WHITE, selectFont())); } textArea.setAlpha((alpha * textAlpha) / 255); } // chaz hack! // private void updateBackgroundColors() { // if (backgroundInstance == null) { // return; // } // // ReadableColor currentColor; // // // switch (state) { // case STATE_DISABLED: // currentColor = bgDisabledColor != null ? bgDisabledColor : bgColor; // break; // case STATE_MOUSEOFF: // currentColor = bgColor; // break; // case STATE_MOUSEON: // currentColor = bgMouseOnColor != null ? bgMouseOnColor : bgColor; // break; // default: // assert false; // return; // } // // if (currentColor != null) { // backgroundInstance.setColor(currentColor); // backgroundInstance.setAlpha(alpha); // } // } /** * @return the selectDown */ public boolean isSelectDown() { return selectDown; } /** * @return the armed */ public boolean isArmed() { return armed; } /** * @param offset the offset to set */ public void setOffset(int x, int y) { if (x == 0 && y == 0) { this.offset = null; } else { this.offset = new Point(x, y); } if (sprite != null) { sprite.setOffset(x, y); } } /** * @return the offset; may be null */ public ReadablePoint getOffset() { return offset; } /** * @return the textBounds */ public Rectangle getTextBounds() { return textBounds; } public void setTextColors(ReadableColor top, ReadableColor bottom) { if (topColor == null) { topColor = new MappedColor(); } if (bottomColor == null) { bottomColor = new MappedColor(); } topColor.setColor(top); bottomColor.setColor(bottom); updateTextColors(); } public void setDisabledTextColors(ReadableColor top, ReadableColor bottom) { if (disabledTopColor == null) { disabledTopColor = new MappedColor(); } if (disabledBottomColor == null) { disabledBottomColor = new MappedColor(); } disabledTopColor.setColor(top); disabledBottomColor.setColor(bottom); updateTextColors(); } public void setMouseOnTextColors(ReadableColor top, ReadableColor bottom) { if (mouseOnTopColor == null) { mouseOnTopColor = new MappedColor(); } if (mouseOnBottomColor == null) { mouseOnBottomColor = new MappedColor(); } mouseOnTopColor.setColor(top); mouseOnBottomColor.setColor(bottom); updateTextColors(); } public void setTextAlpha(int textAlpha) { this.textAlpha = textAlpha; if (textArea != null) { textArea.setAlpha((alpha * textAlpha) / 255); } } public int getTextHeight() { return textArea != null ? textArea.getHeight() : 0; } public boolean isAllCaps() { return allCaps; } public void setAllCaps(boolean allCaps) { this.allCaps = allCaps; } }