Results 1 to 9 of 9
Like Tree2Likes
  • 1 Post By Fubarable
  • 1 Post By pbrockway2

Thread: Swing, Hexagons, and odd drawing O MY

  1. #1
    Souls512 is offline Member
    Join Date
    Apr 2012
    Posts
    5
    Rep Power
    0

    Default Swing, Hexagons, and odd drawing O MY

    I am attempting to make a Hexagon based board.

    My first attempt was a JPanel that I repeatedly drew a hexagon image on. This had slowness issues when placed in a JScrollPane.

    Searching for a new way I found the code below. This seemed pretty sensible so I started playing around with it.

    However, it turns out that when you click one of the hexagons there is an odd painting effect going on. The paintComponent function looks as though it would only paint the area inside the hex, however when clicking a hex it looks as though it paints a blank "Square" that the control would live in and then paints the hexagon.

    Being new to swing I am unsure even how to track down why it isn't simply painting the hexagon.

    Any assistance would be greatly appreciated.

    Java Code:
    package CodeSnips;
    
    
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.FontMetrics;
    import java.awt.Graphics;
    import java.awt.Point;
    import java.awt.Polygon;
    import java.awt.Rectangle;
    import java.awt.event.MouseEvent;
    import javax.swing.Action;
    import javax.swing.Icon;
    import javax.swing.JButton;
    import javax.swing.JComponent;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JToggleButton;
    import javax.swing.SwingUtilities;
    
    /**
     * A six sided toggle button. This is not guaranteed to be a perfect hexagon, it is just guaranteed to have six sides in
     * the form of a hexagon. To be a perfect hexagon the size of this component must have a height to width ratio of
     * 1 to 0.866
     *
     * @author keang
     * @date 5 Jun 2009
     *
     */
    public class HexButton extends JToggleButton
    //public class HexButton extends JComponent
    {
    	private static final long serialVersionUID = 4865976127980106774L;
    
    	private Polygon hexagon = new Polygon();
    
    	
    	//public HexButton
    
    	/**
    	 * @param arg0
    	 */
    	public HexButton(String arg0)
    	{
    	    //super(arg0);
    	}
    
    	
    	@Override
    	public boolean contains(Point p)
        {
    		return hexagon.contains(p);
        }
    
    	@Override
    	public boolean contains(int x, int y)
        {
    		return hexagon.contains(x, y);
        }
    
    	@Override
    	public void setSize(Dimension d)
        {
    		super.setSize(d);
    		calculateCoords();
        }
    
    	@Override
    	public void setSize(int w, int h)
        {
    		super.setSize(w, h);
    		calculateCoords();
        }
    
    	@Override
    	public void setBounds(int x, int y, int width, int height)
        {
    		super.setBounds(x, y, width, height);
    		calculateCoords();
        }
    
    	@Override
    	public void setBounds(Rectangle r)
        {
    		super.setBounds(r);
    		calculateCoords();
        }
    
    	@Override
    	protected void processMouseEvent(MouseEvent e)
        {
    		if ( contains(e.getPoint()) )
    			super.processMouseEvent(e);
        }
    
    	private void calculateCoords()
        {
    		int w = getWidth()-1;
    		int h = getHeight()-1;
    
    		int ratio = (int)(h*.25);
    		int nPoints = 6;
    		int[] hexX = new int[nPoints];
    		int[] hexY = new int[nPoints];
    
    	    agressiveCoords(w, h, ratio, hexX, hexY);
    		//passiveCoords(w, h, ratio, hexX, hexY);
    		
    	
    	    hexagon = new Polygon(hexX, hexY, nPoints);
        }
    
    
    	private void agressiveCoords(int w, int h, int ratio, int[] hexX, int[] hexY) {
    		hexX[0] = w/2;
    	    hexY[0] = 0;
    	    
    	    hexX[1] = w;
    	    hexY[1] = ratio;
    	    
    	    hexX[2] = w;
    	    hexY[2] = h - ratio;
    	    
    	    hexX[3] = w/2;
    	    hexY[3] = h;
    	    
    	    hexX[4] = 0;
    	    hexY[4] = h - ratio;
    	    
    	    hexX[5] = 0;
    	    hexY[5] = ratio;
    	}
    	
    	private void passiveCoords(int w, int h, int ratio, int[] hexX, int[] hexY) {
    		hexY[0] = w/2;
    	    hexX[0] = 0;
    	    
    	    hexY[1] = w;
    	    hexX[1] = ratio;
    	    
    	    hexY[2] = w;
    	    hexX[2] = h - ratio;
    	    
    	    hexY[3] = w/2;
    	    hexX[3] = h;
    	    
    	    hexY[4] = 0;
    	    hexX[4] = h - ratio;
    	    
    	    hexY[5] = 0;
    	    hexX[5] = ratio;
    	}
    		
    	@Override
    	protected void paintComponent(Graphics g)
        {		
    		if ( isSelected() )
            {
    			g.setColor(Color.lightGray);
            }
    		else
            {
    			g.setColor(getBackground());
            }
    
    	    g.fillPolygon(hexagon);
    	
    	    g.setColor(getForeground());
    	    g.drawPolygon(hexagon);
    	
    	    FontMetrics fm = getFontMetrics(getFont());
    	    Rectangle viewR = getBounds();
    	    Rectangle iconR = new Rectangle();
    	    Rectangle textR = new Rectangle();
    	
    	
    	    //SwingUtilities.layoutCompoundLabel(this, fm, getText(), null,
    	    //        SwingUtilities.CENTER, SwingUtilities.CENTER, SwingUtilities.BOTTOM, SwingUtilities.CENTER,
    	    //       viewR, iconR, textR, 0);
    	
    	    //Point loc = getLocation();
    	    //g.drawString(getText(), textR.x-loc.x, textR.y-loc.y+fm.getAscent());
        }
    
    	@Override
    	protected void paintBorder(Graphics g)
        {
    		// do not paint a border
        }
    
    	/**
    	 * Application Entry Point
    	 */
    	public static void main(String[] args)
        {
    	    JFrame frame = new JFrame("Hex Button Test");
    	    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    	
    	    JPanel p = new JPanel();
    	    p.setLayout(new HexLayout(4, 0, 4));
    	
    	    for ( int i = 0; i < 24; i++ )
    	        {
    	        HexButton b = new HexButton("B"+(i+1));
    	        b.setForeground(Color.black);
    	        b.setBackground(Color.green);
    	
    	        p.add(b);
    	        }
    	
    	    frame.add(p);
    	    frame.pack();
    	    frame.setSize(700, 400);
    	    frame.setVisible(true);
        } 
    
    }
    Java Code:
    package CodeSnips;
    
    import java.awt.Component;
    import java.awt.Container;
    import java.awt.Dimension;
    import java.awt.Insets;
    import java.awt.LayoutManager;
    
    /**
     * This layout manager is based on java.awt.GridLayout
     *
     * The <code>GridLayout</code> class is a layout manager that
     * lays out a container's components in a hexagonal grid.
     * The container is divided into equal-sized hexagons,
     * and one component is placed in each hexagon.
     *
     *
     *
     *
     * @author keang
     * @date 5 Jun 2009
     *
     */
    public class HexLayout implements LayoutManager, java.io.Serializable
    {
    	private static final long serialVersionUID = -858342723067286796L;
    
    	/**
    	 * This is the gap (in pixels) which specifies the space
    	 * between components.  They can be changed at any time.
    	 * This should be a non-negative integer.
    	 *
    	 * @serial
    	 * @see #getHgap()
    	 * @see #setHgap(int)
    	 */
    	int cgap;
    	/**
    	 * This is the number of rows specified for the grid.  The number
    	 * of rows can be changed at any time.
    	 * This should be a non negative integer, where '0' means
    	 * 'any number' meaning that the number of Rows in that
    	 * dimension depends on the other dimension.
    	 *
    	 * @serial
    	 * @see #getRows()
    	 * @see #setRows(int)
    	 */
    	int rows;
    	/**
    	 * This is the number of columns specified for the grid.  The number
    	 * of columns can be changed at any time.
    	 * This should be a non negative integer, where '0' means
    	 * 'any number' meaning that the number of Columns in that
    	 * dimension depends on the other dimension.
    	 *
    	 * @serial
    	 * @see #getColumns()
    	 * @see #setColumns(int)
    	 */
    	int cols;
    
    	/**
    	 * Creates a grid layout with a default of one column per component,
    	 * in a single row.
    	 * @since JDK1.1
    	 */
    	public HexLayout() {
    		this(1, 0, 0);
    	}
    
    	/**
    	 * Creates a grid layout with the specified number of rows and
    	 * columns. All components in the layout are given equal size.
    	 * <p>
    	 * One, but not both, of <code>rows</code> and <code>cols</code> can
    	 * be zero, which means that any number of objects can be placed in a
    	 * row or in a column.
    	 * @param     r   the rows, with the value zero meaning
    	 *                   any number of rows.
    	 * @param     c   the columns, with the value zero meaning
    	 *                   any number of columns.
    	 */
    	public HexLayout(int r, int c) {
    		this(r, c, 0);
    	}
    
    	/**
    	 * Creates a grid layout with the specified number of rows and
    	 * columns. All components in the layout are given equal size.
    	 * <p>
    	 * In addition, the gap between components is set to the
    	 * specified value.
    	 * <p>
    	 * One, but not both, of <code>rows</code> and <code>cols</code> can
    	 * be zero, which means that any number of objects can be placed in a
    	 * row or in a column.
    	 * <p>
    	 * All <code>GridLayout</code> constructors defer to this one.
    	 * @param     r   the rows, with the value zero meaning
    	 *                   any number of rows
    	 * @param     c   the columns, with the value zero meaning
    	 *                   any number of columns
    	 * @param     hgap   the gap around the component
    	 * @exception   IllegalArgumentException  if the value of both
    	 *          <code>rows</code> and <code>cols</code> is
    	 *          set to zero
    	 */
    	public HexLayout(int r, int c, int hgap) {
    		if ((r == 0) && (c == 0)) {
    			throw new IllegalArgumentException("rows and cols cannot both be zero");
    		}
    		this.rows = r;
    		this.cols = c;
    		this.cgap = hgap;
    	}
    
    	/**
    	 * Gets the number of rows in this layout.
    	 * @return    the number of rows in this layout
    	 */
    	public int getRows() {
    		return rows;
    	}
    
    	/**
    	 * Sets the number of rows in this layout to the specified value.
    	 * @param        r   the number of rows in this layout
    	 * @exception    IllegalArgumentException  if the value of both
    	 *               <code>rows</code> and <code>cols</code> is set to zero
    	 */
    	public void setRows(int r) {
    		if ((r == 0) && (this.cols == 0)) {
    			throw new IllegalArgumentException("rows and cols cannot both be zero");
    		}
    		this.rows = r;
    	}
    
    	/**
    	 * Gets the number of columns in this layout.
    	 * @return     the number of columns in this layout
    	 */
    	public int getColumns() {
    		return cols;
    	}
    
    	/**
    	 * Sets the number of columns in this layout to the specified value.
    	 * Setting the number of columns has no affect on the layout
    	 * if the number of rows specified by a constructor or by
    	 * the <tt>setRows</tt> method is non-zero. In that case, the number
    	 * of columns displayed in the layout is determined by the total
    	 * number of components and the number of rows specified.
    	 * @param        c   the number of columns in this layout
    	 * @exception    IllegalArgumentException  if the value of both
    	 *               <code>rows</code> and <code>cols</code> is set to zero
    	 */
    	public void setColumns(int c) {
    		if ((c == 0) && (this.rows == 0)) {
    			throw new IllegalArgumentException("rows and cols cannot both be zero");
    		}
    		this.cols = c;
    	}
    
    	/**
    	 * Gets the gap between components.
    	 * @return       the gap between components
    	 */
    	public int getGap() {
    		return cgap;
    	}
    
    	/**
    	 * Sets the gap between components to the specified value.
    	 * @param         gap  the gap between components
    	 */
    	public void setGap(int gap) {
    		this.cgap = gap;
    	}
    
    	/**
    	 * Adds the specified component with the specified name to the layout.
    	 * @param name the name of the component
    	 * @param comp the component to be added
    	 */
    	public void addLayoutComponent(String name, Component comp)
    	{
    		// do nothing
    	}
    
    	/**
    	 * Removes the specified component from the layout.
    	 * @param comp the component to be removed
    	 */
    	public void removeLayoutComponent(Component comp)
    	{
    		// do nothing
    	}
    
    	/**
    	 * Determines the preferred size of the container argument using
    	 * this grid layout.
    	 * <p>
    	 * The preferred width of a grid layout is the largest preferred width
    	 * of all of the components in the container times the number of columns,
    	 * plus the horizontal padding times the number of columns minus one,
    	 * plus the left and right insets of the target container, plus the width
    	 * of half a component if there is more than one row.
    	 * <p>
    	 * The preferred height of a grid layout is the largest preferred height
    	 * of all of the components in the container plus three quarters of the
    	 * largest minimum height of all of the components in the container times
    	 * the number of rows greater than one,
    	 * plus the vertical padding times the number of rows minus one, plus
    	 * the top and bottom insets of the target container.
    	 *
    	 * @param     parent   the container in which to do the layout
    	 * @return    the preferred dimensions to lay out the
    	 *                      subcomponents of the specified container
    	 * @see       java.awt.GridLayout#minimumLayoutSize
    	 * @see       java.awt.Container#getPreferredSize()
    	 */
    	public Dimension preferredLayoutSize(Container parent)
    	{
    		synchronized ( parent.getTreeLock() )
    		{
    			Insets insets = parent.getInsets();
    			int ncomponents = parent.getComponentCount();
    			int nrows = rows;
    			int ncols = cols;
    
    			if ( nrows > 0 )
    			{
    				ncols = (ncomponents + nrows - 1) / nrows;
    			}
    			else
    			{
    				nrows = (ncomponents + ncols - 1) / ncols;
    			}
    
    			int w = 0;
    			int h = 0;
    			for ( int i = 0; i < ncomponents; i++ )
    			{
    				Component comp = parent.getComponent(i);
    				Dimension d = comp.getPreferredSize();
    				if ( w < d.width )
    				{
    					w = d.width;
    				}
    				if ( h < d.height )
    				{
    					h = d.height;
    				}
    			}
    
    			int dx = insets.left + insets.right + ncols * w + (ncols - 1) * cgap;
    			int dy = insets.top + insets.bottom + nrows * h + (nrows - 1) * cgap;
    
    			if ( nrows > 1 )
    			{
    				dx = dx + (int)(w * 0.5f);
    
    				dy /= nrows;
    				dy = dy + (int)(dy * (nrows - 1) * 0.75f);
    			}
    
    			return new Dimension(dx, dy);
    		}
    	}
    
    	/**
    	 * Determines the minimum size of the container argument using this
    	 * grid layout.
    	 * <p>
    	 * The minimum width of a grid layout is the largest minimum width
    	 * of all of the components in the container times the number of columns,
    	 * plus the horizontal padding times the number of columns minus one,
    	 * plus the left and right insets of the target container, plus the width
    	 * of half a component if there is more than one row.
    	 * <p>
    	 * The minimum height of a grid layout is the largest minimum height
    	 * of all of the components in the container plus three quarters of the
    	 * largest minimum height of all of the components in the container times
    	 * the number of rows greater than one,
    	 * plus the vertical padding times the number of rows minus one, plus
    	 * the top and bottom insets of the target container.
    	 *
    	 * @param       parent   the container in which to do the layout
    	 * @return      the minimum dimensions needed to lay out the
    	 *                      subcomponents of the specified container
    	 * @see         java.awt.GridLayout#preferredLayoutSize
    	 * @see         java.awt.Container#doLayout
    	 */
    	public Dimension minimumLayoutSize(Container parent)
    	{
    		synchronized ( parent.getTreeLock() )
    		{
    			Insets insets = parent.getInsets();
    			int ncomponents = parent.getComponentCount();
    			int nrows = rows;
    			int ncols = cols;
    
    			if ( nrows > 0 )
    			{
    				ncols = (ncomponents + nrows - 1) / nrows;
    			}
    			else
    			{
    				nrows = (ncomponents + ncols - 1) / ncols;
    			}
    			int w = 0;
    			int h = 0;
    			for ( int i = 0; i < ncomponents; i++ )
    			{
    				Component comp = parent.getComponent(i);
    				Dimension d = comp.getMinimumSize();
    				if ( w < d.width )
    				{
    					w = d.width;
    				}
    				if ( h < d.height )
    				{
    					h = d.height;
    				}
    			}
    
    			int dx = insets.left + insets.right + ncols * w + (ncols - 1) * cgap;
    			int dy = insets.top + insets.bottom + nrows * h + (nrows - 1) * cgap;
    
    			if ( nrows > 1 )
    			{
    				dx = dx + (int)(w * 0.5f);
    
    				dy /= nrows;
    				dy = dy + (int)(dy * (nrows - 1) * 0.75f);
    			}
    
    			return new Dimension(dx, dy);
    		}
    	}
    
    	/**
    	 * Lays out the specified container using this layout.
    	 * <p>
    	 * This method reshapes the components in the specified target
    	 * container in order to satisfy the constraints of the
    	 * <code>GridLayout</code> object.
    	 * <p>
    	 * The grid layout manager determines the size of individual
    	 * components by dividing the free space in the container into
    	 * equal-sized portions according to the number of rows and columns
    	 * in the layout. The container's free space equals the container's
    	 * size minus any insets and any specified horizontal or vertical
    	 * gap. All components in a grid layout are given the same size.
    	 *
    	 * @param      parent   the container in which to do the layout
    	 * @see        java.awt.Container
    	 * @see        java.awt.Container#doLayout
    	 */
    	public void layoutContainer(Container parent)
    	{
    		synchronized (parent.getTreeLock())
    		{
    			Insets insets = parent.getInsets();
    			int ncomponents = parent.getComponentCount();
    			int nrows = rows;
    			int ncols = cols;
    
    			if ( ncomponents == 0 )
    			{
    				return;
    			}
    			if ( nrows > 0 )
    			{
    				ncols = (ncomponents + nrows - 1) / nrows;
    			}
    			else
    			{
    				nrows = (ncomponents + ncols - 1) / ncols;
    			}
    
    			int w = parent.getWidth() - (insets.left + insets.right);
    			int h = parent.getHeight() - (insets.top + insets.bottom);
    
    			w = (int)((w - (ncols - 1) * cgap) / (ncols + (nrows>1?0.5f:0.0f)));
    
    			float effectiveRows = 1 + ((nrows - 1) * 0.75f);
    			h = (int)((h - (nrows - 1) * cgap) / effectiveRows);
    
    			int xoffset = (w+cgap)/2;
    			int yoffset = (int)(h * 0.75f);
    			boolean staggeredRow = false;
    			
    			for ( int r = 0, y = insets.top; r < nrows; r++, y += yoffset + cgap )
    			{
    				int offset = 0;
    
    				if ( staggeredRow )
    					offset = xoffset;
    
    				for ( int c = 0, x = insets.left; c < ncols; c++, x += w + cgap )
    				{
    					int i = r * ncols + c;
    
    					if ( i < ncomponents )
    					{
    						parent.getComponent(i).setBounds(x+offset, y, w, h);
    					}
    
    				}
    
    				staggeredRow = !staggeredRow;
    			}
    		}
    	}
    
    	/**
    	 * Returns the string representation of this grid layout's values.
    	 * @return     a string representation of this grid layout
    	 */
    	public String toString() {
    		return getClass().getName() + "[gap=" + cgap +
    		",rows=" + rows + ",cols=" + cols + "]";
    	}
    
    }

  2. #2
    Fubarable's Avatar
    Fubarable is offline Moderator
    Join Date
    Jun 2008
    Posts
    19,316
    Blog Entries
    1
    Rep Power
    26

    Default Re: Swing, Hexagons, and odd drawing O MY

    So let me get this straight -- you want us to go through this *heck* of a lot of found code to find out why it isn't working right? Sorry, but this is probably not going to happen. Why not instead write your own app from the ground up, then we'll be more than happy to help you if you need it.

  3. #3
    Souls512 is offline Member
    Join Date
    Apr 2012
    Posts
    5
    Rep Power
    0

    Default Re: Swing, Hexagons, and odd drawing O MY

    I have gone though this code extensively.

    I could rewrite it from the ground up, but I see no benefit to rewriting code that already exists.
    This codes solution to my need seems simple and straight forward.

    Most of the lines are just comments and overloads to keep the control for doing unexpected things.
    I can remove them and re-post if your issue is you saw "WALL OF CODE"


    There is a Hexagon object and a HexagonLayout.

    The Layout simply sets the hexagons up in a panel so they look like a hexagon map by changing where they think they are. Pretty simple really.

    The Hexagon object really just draws the hexagon. It is a JToggleButton but has its paintComponent overridden.


    I just don't get why clicking the toggle button changes/paints on area that is not within the bounds on the hexagon.

  4. #4
    Souls512 is offline Member
    Join Date
    Apr 2012
    Posts
    5
    Rep Power
    0

    Default Re: Swing, Hexagons, and odd drawing O MY

    Well ... it would seem others are not seeing the odd behavior that I am with this code ... good times.

    Guess I will be looking into the Java JDK version I am working with. Currently using Eclipse and jre6.

    Swing, Hexagons, and odd drawing O MY-oddhex.png

  5. #5
    Fubarable's Avatar
    Fubarable is offline Moderator
    Join Date
    Jun 2008
    Posts
    19,316
    Blog Entries
    1
    Rep Power
    26

    Default Re: Swing, Hexagons, and odd drawing O MY

    A problem with using JButtons is that they paint in a rectangular region, even if they are displaying an image that does not extend to the borders of this region.

    I think that this could likely be done much simpler by drawing a hexagonal grid on JPanel and adding a MouseListener to the JPanel and checking to see which hexagon has been pressed and then changing its color.
    Souls512 likes this.

  6. #6
    Souls512 is offline Member
    Join Date
    Apr 2012
    Posts
    5
    Rep Power
    0

    Default Re: Swing, Hexagons, and odd drawing O MY

    That was version 1.0 that I was working on.
    The issue there was that the code became over complex to support large panels placed in a JScrollPane.

    The "paintComponent" would draw "all" hexes even those not on screen. The code to make it only draw the visible ones seems clunky.
    I decided to attempt this other way to see the inherent differences in the two approaches.
    Last edited by Souls512; 04-08-2012 at 09:02 PM.

  7. #7
    Norm's Avatar
    Norm is offline Moderator
    Join Date
    Jun 2008
    Location
    SW Missouri
    Posts
    17,308
    Rep Power
    25

    Default Re: Swing, Hexagons, and odd drawing O MY

    If you don't understand my response, don't ignore it, ask a question.

  8. #8
    pbrockway2 is offline Moderator
    Join Date
    Feb 2009
    Location
    New Zealand
    Posts
    4,565
    Rep Power
    12

    Default Re: Swing, Hexagons, and odd drawing O MY

    I can't reproduce the problem either (with a variety of looks and feels) but you might try setOpaque(true) in the button's constructor as camickr remarks in this thread that the ButtonUI does custom painting when button is pressed.

    I think I would at least try fubarable's suggestion - you draw a bunch of lines in three directions (*), each direction within its own for loop. The "clunkiness" is just a little maths to set the limits of the loops. That's if drawing all the hexagons really does result in appreciably slow drawing code: there must be a lot of them.

    (*) Or hexagonal shapes within a pair of nested loops. That keeps the maths easier.
    Last edited by pbrockway2; 04-08-2012 at 10:33 PM.
    Fubarable likes this.

  9. #9
    Souls512 is offline Member
    Join Date
    Apr 2012
    Posts
    5
    Rep Power
    0

    Default Re: Swing, Hexagons, and odd drawing O MY

    Quote Originally Posted by Norm View Post
    Indeed, was wondering if the two forums were connected.

    Easy fix, changed the HexButton to inherit from JComponent and implemented MouseInputListener for the clicks.
    Works like a charm.

    Thanks for the feedback!

Similar Threads

  1. Drawing with the mouse in the same frame as swing components
    By StillCantPlay in forum AWT / Swing
    Replies: 2
    Last Post: 04-07-2012, 12:12 AM
  2. help in drawing tex
    By Rampletero in forum Advanced Java
    Replies: 8
    Last Post: 03-10-2012, 05:17 PM
  3. Drawing issue with Applet/Swing
    By The_Capn in forum Java Applets
    Replies: 2
    Last Post: 06-18-2011, 05:30 AM
  4. Drawing a selection box using swing
    By edi233 in forum AWT / Swing
    Replies: 6
    Last Post: 05-09-2011, 03:16 PM
  5. drawing images in swing
    By owboateng in forum New To Java
    Replies: 3
    Last Post: 05-09-2011, 02:27 PM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •