Results 1 to 20 of 20
  1. #1
    ravindarjobs is offline Member
    Join Date
    Oct 2007
    Posts
    7
    Rep Power
    0

    Default Drawing charts using swings

    hi friends, i have just moved from vba to java. i am quite new to java. but unexpectedly i was given a task to build a chart for a table stored in oracle database.

    we have to use only swings technology.
    and now my task is,

    There will be a table in oracle database.
    i have to draw a line chart for that table and show to user.

    can any body place me in the correct track towards the solution.


    thank you

  2. #2
    JavaBean's Avatar
    JavaBean is offline Moderator
    Join Date
    May 2007
    Posts
    1,270
    Rep Power
    10

    Default

    You can use Java2D to generate your chart. But there are also some very high quality open-source charting libraries in java. (e.g. JFreeChart)

  3. #3
    hardwired's Avatar
    hardwired is offline Senior Member
    Join Date
    Jul 2007
    Posts
    1,576
    Rep Power
    8

    Default

    Java Code:
    import java.awt.*;
    import java.awt.font.*;
    import java.awt.geom.*;
    import javax.swing.*;
    
    public class LineChart extends JPanel {
        double[] data = { 13.2, 9.2, 4, 16.7, 4.3, 9.5, 12, 5.4 };
        final int PAD  = 25;
        final int TICK  = 2;
        final int STICK = 3;
    
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D)g;
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                                RenderingHints.VALUE_ANTIALIAS_ON);
            drawAxes(g2);
            // plot data
            Point2D.Double lastP = null;
            for(int j = 0; j < data.length; j++) {
                g2.setPaint(Color.red);
                Point2D.Double p = modelToView(j, data[j]);
                g2.fill(new Ellipse2D.Double(p.x-2, p.y-2, 4, 4));
                if(lastP != null) {
                    g2.setPaint(Color.blue);
                    g2.draw(new Line2D.Double(lastP, p));
                }
                lastP = p;
            }
        }
    
        private Point2D.Double modelToView(double x, double y) {
            double w = getWidth();
            double h = getHeight();
            double tx = PAD;
            double ty = h-PAD;
            AffineTransform at = AffineTransform.getTranslateInstance(tx, ty);
            double xScale = (w - 2*PAD)/(data.length-1);
            int max = (int)Math.ceil(getMaxValue());
            double yScale = (h - 2*PAD)/max;
            at.scale(xScale, -yScale);
            Point2D.Double dst = new Point2D.Double();
            at.transform(new Point2D.Double(x, y), dst);
            return dst;
        }
    
        private void drawAxes(Graphics2D g2) {
            double w = getWidth();
            double h = getHeight();
            double xInc = (w - 2*PAD)/(data.length-1);
            int max = (int)Math.ceil(getMaxValue());
            double yInc = (h - 2*PAD)/max;
            // grid lines
            g2.setPaint(new Color(220,230,240));
            // vertical
            for(int j = 0; j <= data.length; j++) {
                double x = PAD + j*xInc;
                g2.draw(new Line2D.Double(x, PAD, x, h-PAD));
            }
            // horizontal
            for(int j = 0; j <= max; j++) {
                double y = PAD + j*yInc;
                g2.draw(new Line2D.Double(PAD, y, w-PAD, y));
            }
            // axes
            g2.setPaint(new Color(51,51,51));
    
            // ordinate
            g2.draw(new Line2D.Double(PAD, PAD, PAD, h-PAD));
            // tick marks
            for(int j = 0; j <= max; j++) {
                double y = PAD + j*yInc;
                g2.draw(new Line2D.Double(PAD, y, PAD-TICK, y));
            }
            // labels
            Font font = g2.getFont().deriveFont(14f);
            g2.setFont(font);
            FontRenderContext frc = g2.getFontRenderContext();
            LineMetrics lm = font.getLineMetrics("0", frc);
            for(int j = 0; j <= max; j++) {
                String s = String.valueOf(max-j);
                float y = (float)(PAD + j*yInc + lm.getDescent());
                float width = (float)font.getStringBounds(s, frc).getWidth();
                float x = (float)(PAD - TICK - STICK - width);
                g2.drawString(s, x, y);
            }
            
            // abcissa
            g2.draw(new Line2D.Double(PAD, h-PAD, w-PAD, h-PAD));
            // tick marks
            for(int j = 0; j <= data.length; j++) {
                double x = PAD + j*xInc;
                g2.draw(new Line2D.Double(x, h-PAD, x, h-PAD+TICK));
            }
            // labels
            for(int j = 0; j <= data.length; j++) {
                String s = String.valueOf(j);
                float width = (float)font.getStringBounds(s, frc).getWidth();
                float x = (float)(PAD + j*xInc - width/2);
                float y = (float)(h-PAD + TICK + STICK + lm.getAscent());
                g2.drawString(s, x, y);
            }
        }
    
        private double getMaxValue() {
            double max = -Double.MAX_VALUE;
            for(int j = 0; j < data.length; j++) {
                if(data[j] > max)
                    max = data[j];
            }
            return max;
        }
    
        public static void main(String[] args) {
            JFrame f = new JFrame();
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.getContentPane().add(new LineChart());
            f.setSize(400,400);
            f.setLocation(200,200);
            f.setVisible(true);
        }
    }

  4. #4
    ravindarjobs is offline Member
    Join Date
    Oct 2007
    Posts
    7
    Rep Power
    0

    Default many many thanks

    many thanks friend.


    is this the same way to build the bar graph and area graph?

    how do i appraoch for them?


    Thank you

  5. #5
    hardwired's Avatar
    hardwired is offline Senior Member
    Join Date
    Jul 2007
    Posts
    1,576
    Rep Power
    8

  6. #6
    ravindarjobs is offline Member
    Join Date
    Oct 2007
    Posts
    7
    Rep Power
    0

    Default

    thank you.


    but a problem here is

    in the first code u gave to me, x-axis spans no of values we enter say 7 values or 8.
    instead my requirement is

    i have table with two columns named
    1. Year
    2. value

    x-axis must span Year column data. i.e x axis must have intervals as data in Year column.

    suppose i have data in year column as

    1995
    1996
    1997
    1998

    then the x-axis should have intervals as
    1995 1996 1997 1998
    instead of no of years(here they r four years)


    i think you got my point.

    can you help me in this one?


    Thank you


    according to this the graph has to be generated.

  7. #7
    hardwired's Avatar
    hardwired is offline Senior Member
    Join Date
    Jul 2007
    Posts
    1,576
    Rep Power
    8

    Default

    Java Code:
    import java.awt.*;
    import java.awt.font.*;
    import java.awt.geom.*;
    import javax.swing.*;
    
    public class LineChart extends JPanel {
        double[] data = { 13.8, 9.2, 4.3, 16.7 };
        int[] years = { 1995, 1996, 1997, 1998 };
        final int PAD  = 25;
        final int TICK  = 2;
        final int STICK = 3;
    
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D)g;
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                                RenderingHints.VALUE_ANTIALIAS_ON);
            drawAxes(g2);
            // plot data
            Point2D.Double lastP = null;
            for(int j = 0; j < data.length; j++) {
                g2.setPaint(Color.red);
                Point2D.Double p = modelToView(j, data[j]);
                g2.fill(new Ellipse2D.Double(p.x-2, p.y-2, 4, 4));
                if(lastP != null) {
                    g2.setPaint(Color.blue);
                    g2.draw(new Line2D.Double(lastP, p));
                }
                lastP = p;
            }
        }
    
        private Point2D.Double modelToView(double x, double y) {
            double w = getWidth();
            double h = getHeight();
            double tx = PAD;
            double ty = h-PAD;
            AffineTransform at = AffineTransform.getTranslateInstance(tx, ty);
            double xScale = (w - 2*PAD)/(data.length-1);
            int max = (int)Math.ceil(getMaxValue());
            double yScale = (h - 2*PAD)/max;
            at.scale(xScale, -yScale);
            Point2D.Double dst = new Point2D.Double();
            at.transform(new Point2D.Double(x, y), dst);
            return dst;
        }
    
        private void drawAxes(Graphics2D g2) {
            double w = getWidth();
            double h = getHeight();
            double xInc = (w - 2*PAD)/(data.length-1);
            int max = (int)Math.ceil(getMaxValue());
            double yInc = (h - 2*PAD)/max;
            // grid lines
            g2.setPaint(new Color(220,230,240));
            // vertical
            for(int j = 0; j <= data.length; j++) {
                double x = PAD + j*xInc;
                g2.draw(new Line2D.Double(x, PAD, x, h-PAD));
            }
            // horizontal
            for(int j = 0; j <= max; j++) {
                double y = PAD + j*yInc;
                g2.draw(new Line2D.Double(PAD, y, w-PAD, y));
            }
            // axes
            g2.setPaint(new Color(51,51,51));
    
            // ordinate
            g2.draw(new Line2D.Double(PAD, PAD, PAD, h-PAD));
            // tick marks
            for(int j = 0; j <= max; j++) {
                double y = PAD + j*yInc;
                g2.draw(new Line2D.Double(PAD, y, PAD-TICK, y));
            }
            // labels
            Font font = g2.getFont().deriveFont(14f);
            g2.setFont(font);
            FontRenderContext frc = g2.getFontRenderContext();
            LineMetrics lm = font.getLineMetrics("0", frc);
            for(int j = 0; j <= max; j++) {
                String s = String.valueOf(max-j);
                float y = (float)(PAD + j*yInc + lm.getDescent());
                float width = (float)font.getStringBounds(s, frc).getWidth();
                float x = (float)(PAD - TICK - STICK - width);
                g2.drawString(s, x, y);
            }
            
            // abcissa
            g2.draw(new Line2D.Double(PAD, h-PAD, w-PAD, h-PAD));
            // tick marks
            for(int j = 0; j <= data.length; j++) {
                double x = PAD + j*xInc;
                g2.draw(new Line2D.Double(x, h-PAD, x, h-PAD+TICK));
            }
            // labels
            for(int j = 0; j < data.length; j++) {
                String s = String.valueOf(years[j]);
                float width = (float)font.getStringBounds(s, frc).getWidth();
                float x = (float)(PAD + j*xInc - width/2);
                float y = (float)(h-PAD + TICK + STICK + lm.getAscent());
                g2.drawString(s, x, y);
            }
        }
    
        private double getMaxValue() {
            double max = -Double.MAX_VALUE;
            for(int j = 0; j < data.length; j++) {
                if(data[j] > max)
                    max = data[j];
            }
            return max;
        }
    
        public static void main(String[] args) {
            JFrame f = new JFrame();
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.getContentPane().add(new LineChart());
            f.setSize(400,400);
            f.setLocation(200,200);
            f.setVisible(true);
        }
    }

  8. #8
    ravindarjobs is offline Member
    Join Date
    Oct 2007
    Posts
    7
    Rep Power
    0

    Default Great.........

    Amazing..........


    now it suits well to my requirement.
    Thank you.

    shall i ask you one doubt again?

    Y-axis values span in the range of 0-max.

    but suppose the values are like this

    Value

    23
    134
    650
    33

    here my my max value is 650, and since the intervals of y-axis is 1, you can imagine how many intervals will come, and y-axis values span very very closely and we cant understand them.

    so i have tried to change the the labels on the Y-axis as some thing like this

    Java Code:
    for(int i=0;i<data.length;i++)
    {
       String s = String.valueOf(data[j]);
                float y = (float)(PAD + j*yInc + lm.getDescent());
                float width = (float)font.getStringBounds(s, frc).getWidth();
                float x = (float)(PAD - TICK - STICK - width);
                g2.drawString(s, x, y);
    }
    but i could not make a solution.

    my view is i have the values as said above.
    there are four values.
    and the max value is 650.
    so now divide 0-650 few intervals
    and then now span the graph.

    i have seen this type of graphs in ms access. and thats why my thought have gone like this.

    suppose the year and values goes like this in the image below


    the graph should go like this below



    I hope you have orkut account.


    Thank you
    Last edited by JavaBean; 11-02-2007 at 09:35 AM. Reason: Code placed inside [code] tag!

  9. #9
    ravindarjobs is offline Member
    Join Date
    Oct 2007
    Posts
    7
    Rep Power
    0

    Default open source

    if it is better to go for a open source libraries, please do suggest one link.
    Jfreechart is one tool. but i could not get a full version.
    so can you tell me one good open source download link
    thank you

  10. #10
    JavaBean's Avatar
    JavaBean is offline Moderator
    Join Date
    May 2007
    Posts
    1,270
    Rep Power
    10

    Default

    No as far as i know, it is totally open-source. The only problem i have seen is that its user guide which is a very valuable resource is to be bought. It is not a too big amount but If you can not buy the user guide then try searching it on google. You will find a little bit older versions of this user guide but i think it is more than enough for any purposes to start. At the end, if you learn basics you can easily obtain missing info there by looking at javadocs..

    Good luck.

  11. #11
    ravindarjobs is offline Member
    Join Date
    Oct 2007
    Posts
    7
    Rep Power
    0

    Default

    yes you are correct. It is a full free version .
    now i down loaded and tried to use it, but when i unzip it i fine many folders and i panic.



    @ Hard Wired :


    hey Hard Wired, i changed for loop of y-axis and got the output as per my requirement.
    i chaged the y-axis lable for loop like this

    Java Code:
    for(int j = 0; j <= max; j=j+max/data.length) {
                String s = String.valueOf(max-j);
                float y = (float)(PAD + j*yInc + lm.getDescent());
                float width = (float)font.getStringBounds(s, frc).getWidth();
                float x = (float)(PAD - TICK - STICK - width);
                g2.drawString(s, x, y);
            }
    now it is working very fine.

    my next step is to draw
    x-axis title,
    y-axis title,
    and a legend.

    if you have any information regarding this, please give me the link.
    i will read them.

    ohh i have forgotten one thing to ask,

    suppose if y-axis contains values like 4000,3456,....(i.e in thousands),
    i am unable to see them completely. only some letters are appearing. like for 4000, i am able to view only 000. 4 appears lightly(only half).
    that is the frame or some thing covers it.
    can we move the graph to right to a distance from current location?


    Thank you?
    Last edited by ravindarjobs; 11-02-2007 at 11:32 AM.

  12. #12
    JavaBean's Avatar
    JavaBean is offline Moderator
    Join Date
    May 2007
    Posts
    1,270
    Rep Power
    10

    Default

    ravindarjobs, please dont forget to use [code] tag for your codes, that will make your posts more readable.

  13. #13
    hardwired's Avatar
    hardwired is offline Senior Member
    Join Date
    Jul 2007
    Posts
    1,576
    Rep Power
    8

    Default

    Java Code:
    import java.awt.*;
    import java.awt.font.*;
    import java.awt.geom.*;
    import javax.swing.*;
    
    public class LineChart extends JPanel {
        double[] data = { 230, 1340, 6500, 330 };
        int[] years = { 1995, 1996, 1997, 1998 };
        final int PAD  = 20;
        final int VPAD = 45;
        final int HPAD = 55;
        final int TICK  = 2;
        final int STICK = 3;
    
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D)g;
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                                RenderingHints.VALUE_ANTIALIAS_ON);
            drawAxes(g2);
            // plot data
            Point2D.Double lastP = null;
            for(int j = 0; j < data.length; j++) {
                g2.setPaint(Color.red);
                Point2D.Double p = modelToView(j, data[j]);
                g2.fill(new Ellipse2D.Double(p.x-2, p.y-2, 4, 4));
                if(lastP != null) {
                    g2.setPaint(Color.blue);
                    g2.draw(new Line2D.Double(lastP, p));
                }
                lastP = p;
            }
        }
    
        private Point2D.Double modelToView(double x, double y) {
            double w = getWidth();
            double h = getHeight();
            double tx = HPAD;
            double ty = h-VPAD;
            AffineTransform at = AffineTransform.getTranslateInstance(tx, ty);
            double xScale = (w - HPAD-PAD)/(data.length-1);
            int max = (int)Math.ceil(getMaxValue());
            double yScale = (h - PAD-VPAD)/max;
            at.scale(xScale, -yScale);
            Point2D.Double dst = new Point2D.Double();
            at.transform(new Point2D.Double(x, y), dst);
            return dst;
        }
    
        private void drawAxes(Graphics2D g2) {
            double w = getWidth();
            double h = getHeight();
            double xInc = (w - HPAD-PAD)/(data.length-1);
            int max = (int)Math.ceil(getMaxValue());
            double yInc = (h - PAD-VPAD)/max;
            // grid lines
            g2.setPaint(new Color(220,230,240));
            // vertical
            for(int j = 0; j <= data.length; j++) {
                double x = HPAD + j*xInc;
                g2.draw(new Line2D.Double(x, VPAD, x, h-VPAD));
            }
            // horizontal
            for(int j = 0; j <= max; j=j+max/data.length) {
                double y = PAD + j*yInc;
                g2.draw(new Line2D.Double(HPAD, y, w-HPAD, y));
            }
            // axes
            g2.setPaint(new Color(51,51,51));
    
            // ordinate
            g2.draw(new Line2D.Double(HPAD, PAD, HPAD, h-VPAD));
            // tick marks
            for(int j = 0; j <= max; j=j+max/data.length) {
                double y = PAD + j*yInc;
                g2.draw(new Line2D.Double(HPAD, y, HPAD-TICK, y));
            }
            // labels
            Font font = g2.getFont().deriveFont(14f);
            g2.setFont(font);
            FontRenderContext frc = g2.getFontRenderContext();
            LineMetrics lm = font.getLineMetrics("0", frc);
            for(int j = 0; j <= max; j=j+max/data.length) {
                String s = String.valueOf(max-j);
                float y = (float)(PAD + j*yInc + lm.getDescent());
                float width = (float)font.getStringBounds(s, frc).getWidth();
                float x = (float)(HPAD - TICK - STICK - width);
                g2.drawString(s, x, y);
            }
            // title
            char[] chars = "big numbers".toCharArray();
            // May look better without the descent.
            float height = lm.getAscent() /*+ lm.getDescent()*/;
            float y0 = PAD + (float)((h - PAD-VPAD - height*chars.length)/2);
            float y = y0 + lm.getAscent();
            for(int j = 0; j < chars.length; j++) {
                g2.drawString(String.valueOf(chars[j]), STICK, y);
                y += /*lm.getDescent() +*/ lm.getAscent();
            }
            
            // abcissa
            g2.draw(new Line2D.Double(HPAD, h-VPAD, w-PAD, h-VPAD));
            // tick marks
            for(int j = 0; j <= data.length; j++) {
                double x = HPAD + j*xInc;
                g2.draw(new Line2D.Double(x, h-VPAD, x, h-VPAD+TICK));
            }
            // labels
            for(int j = 0; j < data.length; j++) {
                String s = String.valueOf(years[j]);
                float width = (float)font.getStringBounds(s, frc).getWidth();
                float x = (float)(HPAD + j*xInc - width/2);
                y = (float)(h-VPAD + TICK + STICK + lm.getAscent());
                g2.drawString(s, x, y);
            }
            // title
            String s = "years";
            float width = (float)font.getStringBounds(s, frc).getWidth();
            float x = HPAD + (float)((w - HPAD-PAD - width)/2);
            y = (float)(h - STICK - lm.getDescent());
            g2.drawString(s, x, y);
        }
    
        private double getMaxValue() {
            double max = -Double.MAX_VALUE;
            for(int j = 0; j < data.length; j++) {
                if(data[j] > max)
                    max = data[j];
            }
            return max;
        }
    
        public static void main(String[] args) {
            JFrame f = new JFrame();
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.getContentPane().add(new LineChart());
            f.setSize(400,400);
            f.setLocation(200,200);
            f.setVisible(true);
        }
    }

  14. #14
    ravindarjobs is offline Member
    Join Date
    Oct 2007
    Posts
    7
    Rep Power
    0

    Default

    Hey Hard Wired, are you from heaven?

    Many thanks friend. i was worried whether whether i expressed my problem correctly.
    but any how you understood and cleared it.

    Many thanks friend.

  15. #15
    kim_kassel is offline Member
    Join Date
    Jul 2009
    Posts
    3
    Rep Power
    0

    Default

    Dear Hardwired , i am using this code of yours to draw a graph , but what i m trying to do is draw the graph in a Jpanel which is triggered after a button is clicked , but the problem that i m facing is , when the Jpanel is invoked its transparent , and and cant be closed while clicking the cross . this button is however contained in another jpanel .
    can u please help in this case , looking forward to get ur reply .thanks.

    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {

    filename=jFileChooser1.getSelectedFile().getName() ;
    reader=new TextfileReader(filename);
    reader.splitter();
    a=reader.getarraymaker();
    Arraymaker.printarrays();



    jPanel2.add(new LineChart());
    jPanel2.repaint();
    jPanel2.setVisible(true);




    JFrame f = new JFrame();
    f.add(new JButton("EXIT",null));
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.getContentPane().add(new LineChart());
    f.setSize(400,400);
    f.setLocation(200,200);
    f.validate();

    f.repaint();

    this.setVisible(false);
    this.setEnabled(false);
    f.setVisible(true);







    }

    /**
    * @param args the command line arguments
    */
    public static void main(String args[]) {
    MainForm m=new MainForm();


    m.setVisible(true);


    }

  16. #16
    hardwired's Avatar
    hardwired is offline Senior Member
    Join Date
    Jul 2007
    Posts
    1,576
    Rep Power
    8

    Default

    but the problem that i m facing is , when the Jpanel is invoked its transparent , and and cant be closed while clicking the cross
    This sounds like a symptom of an infinite loop or something similiar causing high cpu use.

  17. #17
    kim_kassel is offline Member
    Join Date
    Jul 2009
    Posts
    3
    Rep Power
    0

    Default

    Any kind suggestion to get around this infinite loop or whatsoever ?thanks again for your prompt reply though.

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

    Default

    Quote Originally Posted by kim_kassel View Post
    Any kind suggestion to get around this infinite loop or whatsoever ?thanks again for your prompt reply though.
    First find out where it's happening: Either use a debugger, or pepper your code with many println(...) statements and study the results.
    Next after identifying the problem you can usually come up with a solution.

  19. #19
    kim_kassel is offline Member
    Join Date
    Jul 2009
    Posts
    3
    Rep Power
    0

    Default

    thanks a lot buddy
    this code considers only the positive X and Y values , what changes are needed in order to consider both positive and negative values. I am trying to read a file comprising of positive and negative values, and then plot it , not to mention the values can be of any range.thanks again , I really appreciate your kind co-operation.
    Last edited by Fubarable; 07-21-2009 at 11:02 PM.

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

    Default

    You've got some beautiful code to work with already, so I'd play with this code and see if you can get it to work on your own first. If you get stuck, then why not post your attempt here and we can work on it with you.

    One place to start: there's a getMaxValue method already present. Perhaps you want to create a similar getMinValue as well.
    Last edited by Fubarable; 07-21-2009 at 11:06 PM.

Similar Threads

  1. drawing window
    By BlitzA in forum New To Java
    Replies: 1
    Last Post: 01-15-2009, 12:55 PM
  2. java swings
    By emperoraj in forum AWT / Swing
    Replies: 0
    Last Post: 03-26-2008, 11:50 AM
  3. Charts - Pie CHarts
    By null_guy in forum New To Java
    Replies: 0
    Last Post: 03-10-2008, 10:22 PM
  4. Displaying charts in a jsp
    By Priyanka in forum Java Servlet
    Replies: 1
    Last Post: 11-16-2007, 10:59 AM
  5. Charts using JAVA or JSP
    By rahulranjith in forum Java Applets
    Replies: 1
    Last Post: 07-19-2007, 06:37 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
  •