package com.jpii.navalbattle.pavo.gui.controls; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.util.ArrayList; import com.jpii.navalbattle.pavo.PavoHelper; import com.jpii.navalbattle.pavo.gui.events.PMouseListener; /** * Master control class. Essential for all other controls. */ public class Control { protected BufferedImage buffer; protected int width; protected int height; protected int x; protected int y; private Control parent; private boolean isPerPieceUpdateSupported = true; protected boolean lastKnownTransMode = true; private boolean visible = true; protected ArrayList<Control> controls; private static long HANDLE_COUNTER = 0; private long HANDLE = 0; private boolean disposed = false; private Font controlFont = new Font("Arial",0,12); private Color foreColor = Color.black; private Color backColor = new Color(193,172,134); private boolean intermediate = false; private String tooltip = null; protected ArrayList<PMouseListener> pml = new ArrayList<PMouseListener>(); public Control(Control parent) { this.parent = parent; width = 100; height = 100; createBuffer(false); controls = new ArrayList<Control>(); HANDLE = ++HANDLE_COUNTER; } public void addMouseListener(PMouseListener pmls) { pml.add(pmls); } public PMouseListener getMouseListener(int index) { return pml.get(index); } public int getTotalMouseListeners() { return pml.size(); } protected void bufferNeedsIntemediatePaint() { intermediate = true; } /** * NO TOUCHING!!! * @return NO TOUCHING!!! */ public long alo_livrezon_pa_p�m�t() { return HANDLE; } public Control(Control parent, int x, int y) { pml = new ArrayList<PMouseListener>(); this.parent = parent; width = 100; height = 100; createBuffer(false); controls = new ArrayList<Control>(); HANDLE = ++HANDLE_COUNTER; this.x = x; this.y = y; } public Control(Control parent, int x, int y, int width, int height) { pml = new ArrayList<PMouseListener>(); this.parent = parent; controls = new ArrayList<Control>(); HANDLE = ++HANDLE_COUNTER; this.width = width; this.height = height; createBuffer(false); this.x = x; this.y = y; this.width = width; this.height= height; } public void dispose() { for (int c = 0; c < getTotalControls(); c++) { Control cn = getControl(c); if (cn != null) cn.dispose(); } HANDLE = 0; HANDLE_COUNTER--; buffer = null; disposed = true; } public void setFont(Font font) { if (font == null) throw new IllegalArgumentException("Font parameter is null."); controlFont = font; if (isForcingIndividualChanges()) paintUpdate(); } public Font getFont() { return controlFont; } public boolean isVisible() { return visible; } public void setVisible(boolean value) { if (visible != value) { visible = value; paintUpdate(); } } /** * Adds a control to the control. * * Note that adding a control that has already been placed * above the current control (in other words, it would be a * parent), will most likely result in a * <code>IllegalArgumentException</code>. * @param c The control to add to the control. */ public void addControl(Control c) { if (c == null) return; if (c.HANDLE == this.HANDLE) throw new IllegalArgumentException("FATAL ERROR: Cannot add a control to itself!!!"); try { Control badC = searchForControl(this,c.HANDLE,5); if (badC != null) throw new IllegalArgumentException("FATAL ERROR: Cannot add a control to itself!!!"); } catch (Throwable t) { } controls.add(c); repaint(); } public Color getForegroundColor() { return foreColor; } public Color getBackgroundColor() { return backColor; } public void setForegroundColor(Color foreground) { if (!foreground.equals(foreColor)) { foreColor = foreground; if (isForcingIndividualChanges()) paintUpdate(); } } public void setBackgroundColor(Color background) { if (!background.equals(foreColor)) { foreColor = background; if (isForcingIndividualChanges()) paintUpdate(); } } /** * Gets the total number of controls that the current * control controls. * * (That's a mouthful!) * @return The total number of controls. */ public int getTotalControls() { return controls.size(); } /** * Search all controls, and all controls below it for * a control with a certain handle. * * This method is recursive and thus may require some * time to complete. * * May return null. * * @param searchIn The control to search inside of. * @param handle The handle to look for. * @param maxlevels The maximum number of levels to iterate. * @return The control (if it was found.) */ private Control searchForControl(Control searchIn, long handle, int maxlevels) { if (searchIn == null) return null; else { if (maxlevels-- > 0) { for (int c = 0; c < searchIn.getTotalControls(); c++) { Control cn = searchIn.getControl(c); if (cn != null && cn.HANDLE == handle) return cn; else if (cn != null) return searchForControl(cn,handle,maxlevels); } } } return null; } public void removeControl(Control cnt) { boolean removed = false; for (int c = 0; c < getTotalControls(); c++) { Control co = getControl(c); if (co != null && co.HANDLE == cnt.HANDLE) { controls.remove(co); removed = true; c = getTotalControls() + 10; break; } } if (removed) paintUpdate(); } public boolean isDisposed() { return disposed; } /** * Finds a control based on its handle. * @param handle The Handle to search for. */ public Control getControlByHandle(long handle) { for (int c = 0; c < getTotalControls(); c++) { Control cn = getControl(c); if (cn != null && cn.HANDLE == handle) return cn; } return null; } /** * Gets the control from a given index. * @param index The index to retrieve the control at. * @return The control. */ public Control getControl(int index) { return controls.get(index); } /** * Should a repaint occur if a basic method is * called? * * (For example: * * Lets persume there was this situation: * <code> * public void myAwesomeMethod() { * control.setHeight(560); * control.setWidth(600); * control.setBackgroundColor(Color.red); * control.setForegroundColor(Color.black); * ... * } * </code> * * If <code>isForcingIndividualChanges()</code> * is set to true, then all those methods above * will force a re-render every-time one of those * methods is called. * * If <code>isForcingIndividualChanges()</code> * is set to false, then you <i>should</i> do the * following: * * <code> * public void myAwesomeMethod() { * control.setHeight(560); * control.setWidth(600); * control.setBackgroundColor(Color.red); * control.setForegroundColor(Color.black); * ... * control.repaint(); * } * </code> * To force the buffer to update.) * * This method can have its value set using: * <code>forceIndividualChanges(boolean value)</code>. * * @return A value indicating the stuff above. */ public boolean isForcingIndividualChanges() { return isPerPieceUpdateSupported; } /** * Sets a value forcing (or not) individual changes. * * See the documentation for * <code>isForcingIndividualChanges()</code> for more * details. * * @param value The value forcing it or not. */ public void forceIndividualChanges(boolean value) { isPerPieceUpdateSupported = value; } /** * Sets the width of the control. * * This method applies to the * <code>isForcingIndividualChanges()</code> * policy. * * @param width The width to set the control to. */ public void setWidth(int width) { setSize(width,height); } /** * Sets the height of the control. * * This method applies to the * <code>isForcingIndividualChanges()</code> * policy. * * @param height The height to set the control to. */ public void setHeight(int height) { setSize(width,height); } /** * Sets the size of the control. * * This method applies to the * <code>isForcingIndividualChanges()</code> * policy. * * @param width The width to set the control to. * @param height The height to set the control to. */ public void setSize(int width, int height) { boolean flag = false; if (this.width != width || this.height != height) flag = true; this.width = width; this.height = height; if (width == 0 || height == 0) throw new IllegalArgumentException("The width and/or height CANNOT be zero."); if (flag) { createBuffer(lastKnownTransMode); paintUpdate(); } } /** * Gets the width of the control. * @return The width of the control. */ public int getWidth() { return width; } /** * Gets the height of the control. * @return The height of the control. */ public int getHeight() { return height; } /** * Create a temporary Graphics buffer of the * current image buffer. * * Note: This method should not be called * consistently. To update a buffer, overload * the <code>paint(Graphics2D g)</code> method. * * @return A Graphics object. */ public Graphics2D createGraphics() { return PavoHelper.createGraphics(buffer); } /** * This method will call a repaint if it is * needed. */ protected void paintUpdate() { throwBadState(); if (isPerPieceUpdateSupported) { repaint(); } } /** * The paint method. * * @param g The graphics object of the current buffer. */ protected void paint(Graphics2D g) { g.setColor(Color.blue); g.fillRect(0,0,getWidth(),getHeight()); } public void setLocX(int x) { setLoc(x,y); } public Control getParent() { return parent; } public void setLocY(int y) { setLoc(x,y); } public int getLocX() { return x; } public int getLocY() { return y; } public void setLoc(int x, int y) { boolean flag = false; if (this.x != x || this.y != y) flag = true; this.x = x; this.y = y; if (flag) { parentRepaint(); } } /** * This method is called after the sub-controls are rendered. * @param g The graphics object. */ public void paintAfter(Graphics2D g) { } public BufferedImage getBuffer() { return buffer; } /** * Paints sub controls. * @param g The graphics object of the current buffer. */ protected void paintWinControls(Graphics2D g) { for (int c = 0; c < getTotalControls(); c++) { Control cn = getControl(c); if (cn != null && cn.isVisible()) { g.drawImage(cn.getBuffer(), cn.getLocX(), cn.getLocY(), null); } } } /** * Pretty much has no functionality right now. Basically fires <code>onFocus()</code> event. */ public void focus() { onFocus(); } /** * Called when the control regains focus. */ public void onFocus() { } public void onMouseHover(int x, int y) { for (int c = 0; c < getTotalMouseListeners(); c++) { PMouseListener p = pml.get(c); if (p != null) p.mouseHover(x, y); } for (int c = 0; c < getTotalControls(); c++) { Control cn = getControl(c); if (cn != null) { int lx = x - cn.getLocX(); int ly = y - cn.getLocY(); if (lx >= 0 && ly >= 0 && lx < cn.getWidth() && ly < cn.getHeight()) cn.onMouseHover(lx,ly); } } } public void onMouseDown(int x, int y, int buttonid) { for (int c = 0; c < getTotalMouseListeners(); c++) { PMouseListener p = pml.get(c); if (p != null) p.mouseDown(x, y, buttonid); } for (int c = 0; c < getTotalControls(); c++) { Control cn = getControl(c); if (cn != null) { int lx = x - cn.getLocX(); int ly = y - cn.getLocY(); if (lx >= 0 && ly >= 0 && lx < cn.getWidth() && ly < cn.getHeight()) { cn.onMouseDown(lx,ly,buttonid); cn.focus(); } } } } public void onMouseDrag(int x, int y) { for (int c = 0; c < getTotalMouseListeners(); c++) { PMouseListener p = pml.get(c); if (p != null) p.mouseDrag(x, y); } for (int c = 0; c < getTotalControls(); c++) { Control cn = getControl(c); if (cn != null) { int lx = x - cn.getLocX(); int ly = y - cn.getLocY(); if (lx >= 0 && ly >= 0 && lx < cn.getWidth() && ly < cn.getHeight()) cn.onMouseDrag(lx,ly); } } } public void onMouseUp(int x, int y, int buttonid) { for (int c = 0; c < getTotalMouseListeners(); c++) { PMouseListener p = pml.get(c); if (p != null) p.mouseUp(x, y, buttonid); } for (int c = 0; c < getTotalControls(); c++) { Control cn = getControl(c); if (cn != null) { int lx = x - cn.getLocX(); int ly = y - cn.getLocY(); if (lx >= 0 && ly >= 0 && lx < cn.getWidth() && ly < cn.getHeight()) cn.onMouseUp(lx,ly,buttonid); } } } protected void throwBadState() { if (disposed) throw new IllegalStateException("The specified control is disposed. It can no longer be used, however the majority of its" + " properties can most likely be retrieved. (The majority of its properties can most likely not be set either.)"); if (buffer == null) throw new IllegalStateException("Buffer has entered null state."); } /** * Forces the control to repaint. */ public void repaint() { if (intermediate) intermediate = false; throwBadState(); Graphics2D g = createGraphics(); paint(g); paintWinControls(g); paintAfter(g); g.dispose(); parentRepaint(); if (intermediate) repaint(); } public void parentRepaint() { if (parent != null && !parent.isDisposed()) { parent.repaint(); } } public void setToolTip(String message) { tooltip = message; } public String getToolTip() { return tooltip; } protected void createBuffer(boolean transparencyEnabled) { lastKnownTransMode = transparencyEnabled; if (buffer != null) buffer.flush(); if (transparencyEnabled) { buffer = new BufferedImage(getWidth(),getHeight(),BufferedImage.TYPE_4BYTE_ABGR); } else { buffer = new BufferedImage(getWidth(),getHeight(),BufferedImage.TYPE_3BYTE_BGR); } } }