Source Code: Paint Program

The source code files can be viewed in a text editor like Notepad++. Or, the source code can be compiled and run in an IDE environment like Eclipse. Download Source Code


PaintApp.java
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
/*****************************************************************************
*   Lab13: The Paint Program
*   Name: Samantha Yu
*   Submitted to AP Computer Science 12: January 27, 2015
*   Last modified: April 4, 2015
*   Teacher: Christopher Slowley
******************************************************************************/
 
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Frame;
import java.awt.event.WindowListener;
import java.awt.event.WindowEvent;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
 
public class PaintApp extends Frame implements WindowListener, MouseListener, MouseMotionListener {
    /**
     *
     */
    private static final long serialVersionUID = 5650761267721782655L;
     
    // Used when creating the frame of the paint program
    private int appWidth, appHeight, canvasHeight;
    private Menu menuBar;
    private Tools toolbar;
     
    private int menuHeight;
    private Rectangle canvasRect;   // Canvas is not visible and does not include menu bar
     
    private boolean firstPaint, menuChange, continuePaint;  // Used as "switches" during program execution
    private int oldX, oldY, newX, newY;     // Used on mouse events
 
    private Graphics2D g2Frame;
        // The Graphics2D class provides "more sophisticated control over geometry, coordinate transformations, color management, and text layout"
        // (see "http://docs.oracle.com/javase/7/docs/api/java/awt/Graphics2D.html") than its superclass, the Graphics class,
        // Such as being able to paint lines that have different edges
    private Layers layersArray;
     
    public static void main(String args[]) {    // The main method handles the application's framework (such as event listeners) and makes the window visible
        PaintApp paintProgram = new PaintApp();
        paintProgram.addWindowListener(paintProgram);
        paintProgram.addMouseListener(paintProgram);
        paintProgram.addMouseMotionListener(paintProgram);
         
        paintProgram.appWidth = 761;
        paintProgram.appHeight = 380;
        paintProgram.setSize(paintProgram.appWidth, paintProgram.appHeight);
         
        paintProgram.setVisible(true);
        paintProgram.createApp();
    }
     
    public void createApp() {   // Menu bar, canvas, layers array, and toolbar are created
        menuHeight = 75;
        createCanvas();
        layersArray = new Layers(this);
        // The menu bar's selection boxes are created but not the buttons that you can see; i.e. buttons are invisible
        menuBar = new Menu(this, layersArray);
        toolbar = new Tools(this, menuBar, layersArray);
        firstPaint = true;
    }
     
    public void paint(Graphics g) {     // Called after paintProgram became visible and whenever the graphical output needs to be updated or repainted
        g2Frame = (Graphics2D) g;       // The abstract Graphics2D class extends the abstract Graphics class
 
        if (firstPaint) {  
            menuBar.createMenu();       // Menu items are drawn onto the menu off-screen image
            g2Frame.drawImage(menuBar.menuImage, 0, 0, this);   // Off-screen image is drawn onto the application; menu becomes visible
             
            firstPaint = false;
            continuePaint = true;
        }
        else {
            if (continuePaint) {    // If a user's mouse did not click inside the menu, the mouse paints with the user's selected tool, size, and color
                toolbar.useTool(menuBar.getNumTool(), oldX, oldY, newX, newY);
            }
            else // If a menu item is selected, then the area underneath the menu is not painted
                continuePaint = true;   // When the user selects a large size for their tool, then the canvas will not be accidentally painted.
            }
             
            paintLayers(g2Frame);
             
            if (menuChange) {   // If the user has selected a menu item, the menu bar is updated and redrawn
                menuBar.changeToolMenu();
                menuChange = false;
            }
             
            g2Frame.drawImage(menuBar.menuImage, 0, 0, this); // Prevents the menu bar from disappearing when the application has gone inactive or been resized
        }
    }
     
    public void paintLayers(Graphics2D g2Frame) {   // When a layer is created or deleted, or the user decides to switch to a different layer
        if (layersArray.deleteLayer) {  // A layer is deleted (can be any layer, not just the last layer in imageArray, that is not the only layer left)
            g2Frame.drawImage(layersArray.getImageArray().get(layersArray.getcLayerNum()), 0, getMenuHeight(), this);       // Draws the blank deleted layer
                // Otherwise, the deleted layer will remain and is not erased on screen
             
            for (int k = 0; k < layersArray.getImageArray().size(); k++) {          // Then draws the rest of the layers in ascending order
                if (k!=layersArray.getcLayerNum()) {
                    g2Frame.drawImage(layersArray.getImageArray().get(k), 0, getMenuHeight(), this);
                }
            }
             
            layersArray.deleteLayer = false;
            layersArray.getImageArray().remove(layersArray.getcLayerNum());         // Removes the blank deleted layer from imageArray
             
            if (layersArray.getcLayerNum() == layersArray.getImageArray().size())   // If the deleted layer was the last layer, then decrease cLayerNum
                layersArray.setcLayerNum(layersArray.getcLayerNum() - 1);           // If not, then cLayerNum is not decreased
                // eg If the third layer is deleted, then the next layer which used to be the fourth layer is now the third layer
             
            g2Frame.drawImage(layersArray.getImageArray().get(layersArray.getcLayerNum()), 0, getMenuHeight(), this);   // The current layer is drawn last for visibility
        }
        else // If you are not deleting a layer, update only the current layer's graphics.
                // If you constantly redrawing the other layers, then those other layers will flicker in the background
            g2Frame.drawImage(layersArray.getImageArray().get(layersArray.getcLayerNum()), 0, getMenuHeight(), this);
        }
         
        if (layersArray.changeLayer) {  // If a layer is created or deleted, or the user switches to the previous or next layer
            menuBar.changeLayerMenu();  // Layer status is updated
            layersArray.changeLayer = false;
        }
    }
     
    public void windowClosing(WindowEvent e) {
        dispose();
        System.exit(0); // Normal exit of program
    }
     
    // These window events are required to be implemented, because this class implements the WindowListener
    // However, this paint program does not need these window events so these methods are left blank
    public void windowOpened(WindowEvent e){}
    public void windowIconified(WindowEvent e){}
    public void windowClosed(WindowEvent e){}
    public void windowDeiconified(WindowEvent e){}
    public void windowActivated(WindowEvent e){}
    public void windowDeactivated(WindowEvent e){}
     
    private void createCanvas() {   // Creates the canvas that the user can draw on; user cannot draw on menu
        canvasHeight = appHeight - getMenuHeight();
        canvasRect = new Rectangle(0,getMenuHeight(),appWidth, canvasHeight);
    }
     
    public int getAppWidth() {      // Returns the private appWidth attribute
        return appWidth;
    }
     
    public int getCanvasHeight() {  // Returns the private canvasHeight attribute
        return canvasHeight;
    }
     
    public int getMenuHeight() {    // Returns the private menuHeight attribute
        return menuHeight;
    }
     
    // These mouse events are required to be implemented, because this class implements the MouseListener and MouseMotionListener
    // However, this paint program does not need these mouse events so these methods are left blank
    public void mouseClicked(MouseEvent e) {}   // When the mouse button is pressed down then released
    public void mouseEntered(MouseEvent e) {}
    public void mouseExited(MouseEvent e) {}
    public void mouseMoved(MouseEvent e) {}     // When the mouse is moving but none of the buttons are held down
     
    public void mousePressed(MouseEvent e) {    // When any of the mouse buttons are pressed down
        menuBar.select(e.getX(), e.getY());
        newX = e.getX();
        newY = e.getY();
        oldX = e.getX(); 
        oldY = e.getY();
         
        if (menuBar.menuRect.contains(e.getX(),e.getY())) {
            continuePaint = false// After the user selects a menu item, continuePaint will prevent the area under the menu from getting painted.
            menuChange = true;
            repaint();
        }
         
        if (canvasRect.contains(e.getX(), e.getY())) {  // The application repaints if the user clicks inside the canvas
            repaint();
        }
    }
     
    public void mouseDragged(MouseEvent e) {    // When any of the mouse buttons are held down while the mouse is dragged
        oldX = newX;
        oldY = newY;
        newX = e.getX();
        newY = e.getY();
 
        if (canvasRect.contains(e.getX(), e.getY())) {  // The application repaints only if the user drags the mouse inside the canvas
            repaint();
        }
    }
     
    public void mouseReleased(MouseEvent e) {       // When any of the mouse buttons are lifted up
        newX = e.getX();
        newY = e.getY();
 
        if (canvasRect.contains(e.getX(), e.getY())) {  // The application repaints only if the user lifts the mouse inside the canvas
            repaint();
        }
    }
 
    public void update(Graphics g) {
        // When repaint() is called, the update() then calls paint()
        // Since "default implementation of update() clears the Component's background before calling paint()",
        // I removed the background layers from flickering by drawing the layers here
        for (int k = 0; k < layersArray.getImageArray().size(); k++) {
            g2Frame.drawImage(layersArray.getImageArray().get(k), 0, getMenuHeight(), this);
        }
        paint(g);
    }
}
 
						/*****************************************************************************
						*   Lab13: The Paint Program
						*   Name: Samantha Yu
						*   Submitted to AP Computer Science 12: January 27, 2015
						*   Last modified: April 4, 2015
						*   Teacher: Christopher Slowley
						******************************************************************************/

						import java.awt.Graphics;
						import java.awt.Graphics2D;
						import java.awt.Rectangle;
						import java.awt.Frame;
						import java.awt.event.WindowListener;
						import java.awt.event.WindowEvent;
						import java.awt.event.MouseMotionListener;
						import java.awt.event.MouseListener;
						import java.awt.event.MouseEvent;

						public class PaintApp extends Frame implements WindowListener, MouseListener, MouseMotionListener {
							/**
							 * 
							 */
							private static final long serialVersionUID = 5650761267721782655L;
							
							// Used when creating the frame of the paint program
							private int appWidth, appHeight, canvasHeight;
							private Menu menuBar;
							private Tools toolbar;
							
							private int menuHeight;
							private Rectangle canvasRect;   // Canvas is not visible and does not include menu bar
							
							private boolean firstPaint, menuChange, continuePaint;  // Used as "switches" during program execution
							private int oldX, oldY, newX, newY;     // Used on mouse events

							private Graphics2D g2Frame;
								// The Graphics2D class provides "more sophisticated control over geometry, coordinate transformations, color management, and text layout"
								// (see "http://docs.oracle.com/javase/7/docs/api/java/awt/Graphics2D.html") than its superclass, the Graphics class,
								// Such as being able to paint lines that have different edges
							private Layers layersArray;
							
							public static void main(String args[]) {    // The main method handles the application's framework (such as event listeners) and makes the window visible
								PaintApp paintProgram = new PaintApp();
								paintProgram.addWindowListener(paintProgram);
								paintProgram.addMouseListener(paintProgram);
								paintProgram.addMouseMotionListener(paintProgram);
								
								paintProgram.appWidth = 761;
								paintProgram.appHeight = 380;
								paintProgram.setSize(paintProgram.appWidth, paintProgram.appHeight);
								
								paintProgram.setVisible(true);
								paintProgram.createApp();
							}
							
							public void createApp() {   // Menu bar, canvas, layers array, and toolbar are created
								menuHeight = 75;
								createCanvas();
								layersArray = new Layers(this);
								// The menu bar's selection boxes are created but not the buttons that you can see; i.e. buttons are invisible
								menuBar = new Menu(this, layersArray);
								toolbar = new Tools(this, menuBar, layersArray);
								firstPaint = true;
							}
							
							public void paint(Graphics g) {     // Called after paintProgram became visible and whenever the graphical output needs to be updated or repainted
								g2Frame = (Graphics2D) g;       // The abstract Graphics2D class extends the abstract Graphics class

								if (firstPaint) {   
									menuBar.createMenu();       // Menu items are drawn onto the menu off-screen image
									g2Frame.drawImage(menuBar.menuImage, 0, 0, this);   // Off-screen image is drawn onto the application; menu becomes visible
									
									firstPaint = false;
									continuePaint = true;
								}
								else {
									if (continuePaint) {    // If a user's mouse did not click inside the menu, the mouse paints with the user's selected tool, size, and color
										toolbar.useTool(menuBar.getNumTool(), oldX, oldY, newX, newY);
									}
									else {  // If a menu item is selected, then the area underneath the menu is not painted
										continuePaint = true;   // When the user selects a large size for their tool, then the canvas will not be accidentally painted.
									}
									
									paintLayers(g2Frame);
									
									if (menuChange) {   // If the user has selected a menu item, the menu bar is updated and redrawn
										menuBar.changeToolMenu();
										menuChange = false;
									}
									
									g2Frame.drawImage(menuBar.menuImage, 0, 0, this); // Prevents the menu bar from disappearing when the application has gone inactive or been resized
								}
							}
							
							public void paintLayers(Graphics2D g2Frame) {   // When a layer is created or deleted, or the user decides to switch to a different layer
								if (layersArray.deleteLayer) {  // A layer is deleted (can be any layer, not just the last layer in imageArray, that is not the only layer left)
									g2Frame.drawImage(layersArray.getImageArray().get(layersArray.getcLayerNum()), 0, getMenuHeight(), this);       // Draws the blank deleted layer
										// Otherwise, the deleted layer will remain and is not erased on screen
									
									for (int k = 0; k < layersArray.getImageArray().size(); k++) {          // Then draws the rest of the layers in ascending order
										if (k!=layersArray.getcLayerNum()) {
											g2Frame.drawImage(layersArray.getImageArray().get(k), 0, getMenuHeight(), this);
										}
									}
									
									layersArray.deleteLayer = false;
									layersArray.getImageArray().remove(layersArray.getcLayerNum());         // Removes the blank deleted layer from imageArray
									
									if (layersArray.getcLayerNum() == layersArray.getImageArray().size())   // If the deleted layer was the last layer, then decrease cLayerNum
										layersArray.setcLayerNum(layersArray.getcLayerNum() - 1);           // If not, then cLayerNum is not decreased
										// eg If the third layer is deleted, then the next layer which used to be the fourth layer is now the third layer
									
									g2Frame.drawImage(layersArray.getImageArray().get(layersArray.getcLayerNum()), 0, getMenuHeight(), this);   // The current layer is drawn last for visibility
								}
								else {  // If you are not deleting a layer, update only the current layer's graphics.
										// If you constantly redrawing the other layers, then those other layers will flicker in the background
									g2Frame.drawImage(layersArray.getImageArray().get(layersArray.getcLayerNum()), 0, getMenuHeight(), this);
								}
								
								if (layersArray.changeLayer) {  // If a layer is created or deleted, or the user switches to the previous or next layer
									menuBar.changeLayerMenu();  // Layer status is updated
									layersArray.changeLayer = false;
								}
							}
							
							public void windowClosing(WindowEvent e) {
								dispose();
								System.exit(0); // Normal exit of program
							}
							
							// These window events are required to be implemented, because this class implements the WindowListener
							// However, this paint program does not need these window events so these methods are left blank
							public void windowOpened(WindowEvent e){}
							public void windowIconified(WindowEvent e){}
							public void windowClosed(WindowEvent e){}
							public void windowDeiconified(WindowEvent e){}
							public void windowActivated(WindowEvent e){}
							public void windowDeactivated(WindowEvent e){}
							
							private void createCanvas() {   // Creates the canvas that the user can draw on; user cannot draw on menu
								canvasHeight = appHeight - getMenuHeight();
								canvasRect = new Rectangle(0,getMenuHeight(),appWidth, canvasHeight);
							}
							
							public int getAppWidth() {      // Returns the private appWidth attribute
								return appWidth;
							}
							
							public int getCanvasHeight() {  // Returns the private canvasHeight attribute
								return canvasHeight;
							}
							
							public int getMenuHeight() {    // Returns the private menuHeight attribute
								return menuHeight;
							}
							
							// These mouse events are required to be implemented, because this class implements the MouseListener and MouseMotionListener
							// However, this paint program does not need these mouse events so these methods are left blank
							public void mouseClicked(MouseEvent e) {}   // When the mouse button is pressed down then released
							public void mouseEntered(MouseEvent e) {}
							public void mouseExited(MouseEvent e) {}
							public void mouseMoved(MouseEvent e) {}     // When the mouse is moving but none of the buttons are held down
							
							public void mousePressed(MouseEvent e) {    // When any of the mouse buttons are pressed down
								menuBar.select(e.getX(), e.getY());
								newX = e.getX();
								newY = e.getY();
								oldX = e.getX();  
								oldY = e.getY();
								
								if (menuBar.menuRect.contains(e.getX(),e.getY())) {
									continuePaint = false;  // After the user selects a menu item, continuePaint will prevent the area under the menu from getting painted.
									menuChange = true;
									repaint();
								}
								
								if (canvasRect.contains(e.getX(), e.getY())) {  // The application repaints if the user clicks inside the canvas
									repaint();
								}
							}
							
							public void mouseDragged(MouseEvent e) {    // When any of the mouse buttons are held down while the mouse is dragged
								oldX = newX;
								oldY = newY;
								newX = e.getX();
								newY = e.getY();

								if (canvasRect.contains(e.getX(), e.getY())) {  // The application repaints only if the user drags the mouse inside the canvas
									repaint();
								}
							}
							
							public void mouseReleased(MouseEvent e) {       // When any of the mouse buttons are lifted up
								newX = e.getX();
								newY = e.getY();

								if (canvasRect.contains(e.getX(), e.getY())) {  // The application repaints only if the user lifts the mouse inside the canvas
									repaint();
								}
							}

							public void update(Graphics g) {
								// When repaint() is called, the update() then calls paint()
								// Since "default implementation of update() clears the Component's background before calling paint()", 
								// (see http://journals.ecs.soton.ac.uk/java/tutorial/ui/drawing/update.html)
								// I removed the background layers from flickering by drawing the layers here
								for (int k = 0; k < layersArray.getImageArray().size(); k++) {
									g2Frame.drawImage(layersArray.getImageArray().get(k), 0, getMenuHeight(), this);
								}
								paint(g);
							}
						}
						
Layers.java
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/*****************************************************************************
*   Lab13: The Paint Program: Layers class
*   Name: Samantha Yu
*   Submitted to AP Computer Science 12: January 27, 2015
*   Last modified: April 4, 2015
*   Teacher: Christopher Slowley
******************************************************************************/
 
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.JOptionPane;
 
public class Layers {
    PaintApp paintLab;  // A PaintApp object has only one instance and allows this class to access its non-static methods and attributes
        // I did not make PaintApp's attributes static, because those attributes refer to the one and same object, the paint program
        // I did not want to place the Menu's methods into PaintApp, because those methods belong in its own separate category
        // So, this Layers class must be able to access and change the graphical output of PaintApp such as repaint()
     
    private ArrayList<BufferedImage> imageArray;    // Stores buffered images of each layer
        // I decided to use the BufferedImage class instead of its superclass, the Image class, due to its ability to include transparency
    private int cLayerNum;  // The index, cLayerNum, of imageArray allows access to the current layer and makes a graphical context for that layer
     
    public boolean deleteLayer, changeLayer;
     
    public Layers(PaintApp pLab) {  // Creates an ArrayList of BufferedImages to contain the layers and creates an initial layer
        paintLab = pLab;
        imageArray = new ArrayList<BufferedImage>();
         
        BufferedImage bImage = new BufferedImage(paintLab.getAppWidth(), paintLab.getCanvasHeight(), BufferedImage.TYPE_INT_ARGB);
        imageArray.add(bImage);     // Adds the first layer
        cLayerNum = 0;
    }
     
    public void newLayer() {        // Creates a new layer after the current layer
        BufferedImage bImage = new BufferedImage(paintLab.getAppWidth(), paintLab.getCanvasHeight(), BufferedImage.TYPE_INT_ARGB);
        cLayerNum++;
        imageArray.add(cLayerNum,bImage);   // Adds that layer after the current layer
        deleteLayer = false;
        changeLayer = true;
        paintLab.repaint();
    }
     
    public void previousLayer() {   // User switches to the previous layer
        if (cLayerNum > 0) {        // If the previous layer's index exists in imageArray
            cLayerNum--;
            changeLayer = true;
        }
        else // Prevents the user from accessing a layer that does not exist and getting an index out of bounds error
            JOptionPane.showMessageDialog(null, "Sorry, but you cannot go to a layer that does not exist yet.");
        }
        paintLab.repaint(); // The graphical output is repainted, because when the notification pops up it erases some of the graphics
            // Also, this allows for the previous layer's graphics to now be at the top and fully visible
    }
     
    public void nextLayer() {   // User switches to the next layer
        if (cLayerNum < imageArray.size() - 1) {    // If the next layer's index exists in imageArray
            cLayerNum++;
            changeLayer = true;
        }
        else {
            JOptionPane.showMessageDialog(null, "Sorry, but you cannot go to a layer that does not exist yet.");
        }
        paintLab.repaint(); // The graphical output is repainted, because when the notification pops up it erases some of the graphics
            // Also, this allows for the previous layer's graphics to now be at the top and fully visible
    }
     
    public void deleteLayer() { // User deletes the current layer
        if ((cLayerNum != imageArray.size() - 1) || (cLayerNum > 0)) {  // If the current layer is not the only layer left
            BufferedImage bImage = new BufferedImage(paintLab.getAppWidth(), paintLab.getCanvasHeight(), BufferedImage.TYPE_INT_ARGB);
            Graphics2D buffg = (Graphics2D) bImage.getGraphics();
            buffg.fillRect(0, 0, paintLab.getAppWidth(), paintLab.getCanvasHeight());   // Creates a blank image the size of the canvas
             
            imageArray.set(cLayerNum, bImage);  // Sets the current layer to this blank image
            deleteLayer = true;
            changeLayer = true;
            paintLab.repaint(); // The blank image (user-requested deleted layer) clears the canvas and the other layers are redrawn on top
        }
        else {      // User cannot delete the only layer
            JOptionPane.showMessageDialog(null, "Sorry, but you cannot delete your only layer.");
            paintLab.repaint();
        }
    }
     
    public ArrayList<BufferedImage> getImageArray() {   // Returns the private imageArray
        return imageArray;
    }
     
    public int getcLayerNum() { // Returns the private cLayerNum attribute, the current index of imageArray, so that other classes can properly access cLayerNum
        return cLayerNum;
    }
     
    public void setcLayerNum(int cNum) {    // Sets the cLayerNum to the new current layer's index
        cLayerNum = cNum;
    }
}
 
						/*****************************************************************************
						*   Lab13: The Paint Program: Layers class
						*   Name: Samantha Yu
						*   Submitted to AP Computer Science 12: January 27, 2015
						*   Last modified: April 4, 2015
						*   Teacher: Christopher Slowley
						******************************************************************************/

						import java.awt.Graphics2D;
						import java.awt.image.BufferedImage;
						import java.util.ArrayList;
						import javax.swing.JOptionPane;

						public class Layers {
							PaintApp paintLab;  // A PaintApp object has only one instance and allows this class to access its non-static methods and attributes
								// I did not make PaintApp's attributes static, because those attributes refer to the one and same object, the paint program
								// I did not want to place the Menu's methods into PaintApp, because those methods belong in its own separate category
								// So, this Layers class must be able to access and change the graphical output of PaintApp such as repaint()
							
							private ArrayList<BufferedImage> imageArray;    // Stores buffered images of each layer
								// I decided to use the BufferedImage class instead of its superclass, the Image class, due to its ability to include transparency
							private int cLayerNum;  // The index, cLayerNum, of imageArray allows access to the current layer and makes a graphical context for that layer
							
							public boolean deleteLayer, changeLayer;
							
							public Layers(PaintApp pLab) {  // Creates an ArrayList of BufferedImages to contain the layers and creates an initial layer
								paintLab = pLab;
								imageArray = new ArrayList<BufferedImage>();
								
								BufferedImage bImage = new BufferedImage(paintLab.getAppWidth(), paintLab.getCanvasHeight(), BufferedImage.TYPE_INT_ARGB);
								imageArray.add(bImage);     // Adds the first layer
								cLayerNum = 0;
							}
							
							public void newLayer() {        // Creates a new layer after the current layer
								BufferedImage bImage = new BufferedImage(paintLab.getAppWidth(), paintLab.getCanvasHeight(), BufferedImage.TYPE_INT_ARGB);
								cLayerNum++;
								imageArray.add(cLayerNum,bImage);   // Adds that layer after the current layer
								deleteLayer = false;
								changeLayer = true;
								paintLab.repaint();
							}
							
							public void previousLayer() {   // User switches to the previous layer
								if (cLayerNum > 0) {        // If the previous layer's index exists in imageArray
									cLayerNum--;
									changeLayer = true;
								}
								else {  // Prevents the user from accessing a layer that does not exist and getting an index out of bounds error
									JOptionPane.showMessageDialog(null, "Sorry, but you cannot go to a layer that does not exist yet.");
								}
								paintLab.repaint(); // The graphical output is repainted, because when the notification pops up it erases some of the graphics
									// Also, this allows for the previous layer's graphics to now be at the top and fully visible
							}
							
							public void nextLayer() {   // User switches to the next layer
								if (cLayerNum < imageArray.size() - 1) {    // If the next layer's index exists in imageArray
									cLayerNum++;
									changeLayer = true;
								}
								else {
									JOptionPane.showMessageDialog(null, "Sorry, but you cannot go to a layer that does not exist yet.");
								}
								paintLab.repaint(); // The graphical output is repainted, because when the notification pops up it erases some of the graphics
									// Also, this allows for the previous layer's graphics to now be at the top and fully visible
							}
							
							public void deleteLayer() { // User deletes the current layer
								if ((cLayerNum != imageArray.size() - 1) || (cLayerNum > 0)) {  // If the current layer is not the only layer left
									BufferedImage bImage = new BufferedImage(paintLab.getAppWidth(), paintLab.getCanvasHeight(), BufferedImage.TYPE_INT_ARGB);
									Graphics2D buffg = (Graphics2D) bImage.getGraphics();
									buffg.fillRect(0, 0, paintLab.getAppWidth(), paintLab.getCanvasHeight());   // Creates a blank image the size of the canvas
									
									imageArray.set(cLayerNum, bImage);  // Sets the current layer to this blank image
									deleteLayer = true;
									changeLayer = true;
									paintLab.repaint(); // The blank image (user-requested deleted layer) clears the canvas and the other layers are redrawn on top
								}
								else {      // User cannot delete the only layer
									JOptionPane.showMessageDialog(null, "Sorry, but you cannot delete your only layer.");
									paintLab.repaint();
								}
							}
							
							public ArrayList<BufferedImage> getImageArray() {   // Returns the private imageArray
								return imageArray;
							}
							
							public int getcLayerNum() { // Returns the private cLayerNum attribute, the current index of imageArray, so that other classes can properly access cLayerNum
								return cLayerNum;
							}
							
							public void setcLayerNum(int cNum) {    // Sets the cLayerNum to the new current layer's index
								cLayerNum = cNum;
							}
						}
						
Menu.java
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
/*****************************************************************************
*   Lab13: The Paint Program: Menu class
*   Name: Samantha Yu
*   Submitted to AP Computer Science 12: January 27, 2015
*   Last modified: April 4, 2015
*   Teacher: Christopher Slowley
******************************************************************************/
 
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import javax.swing.JColorChooser;
import javax.swing.JOptionPane;
import javax.swing.colorchooser.AbstractColorChooserPanel;
 
public class Menu {
    // Menu buttons are not physically seen, but are the "click-able" items
    Rectangle menuRect, colorRect, sizeRect;
    Rectangle pencilRect, brushRect, sprayRect;
    Rectangle newRect, prevRect, nextRect, delRect;
     
    // Menu appearance
    Color menuColor, paintColor, toolColor, sizeColor, fontColor, layerBtnColor, selectColor;
    Font btnFont;
    private int numTool, strokeSize;
     
    String stringStroke;        // Used when getting the color from dialog boxes
     
    BufferedImage menuImage;    // To prevent the menu bar being erased when the window is no longer active, menuImage will store the current image
    Graphics2D g2Menu;          // g2Menu will paint graphics onto that image when necessary
     
    PaintApp paintLab;  // A PaintApp object has only one instance and allows this class to access its non-static methods and attributes
        // I did not make PaintApp's attributes static, because those attributes refer to the one and same object, the paint program
        // I did not want to place the Menu's methods into PaintApp, because those methods belong in its own separate category
        // So, this Menu class must be able to access the dimensions of PaintApp such as the get methods like getAppWidth()
    Layers layersArray;
     
    public Menu(PaintApp pLab, Layers lArray) { // When the menu object is constructed, the invisible but "click-able" buttons are also constructed
        paintLab = pLab;        // Initializes paintLab to the actual PaintApp, and layersArray to the actual layers array
        layersArray = lArray;   // So that Menu class' methods can access the non-static fields and methods, such as imageArray
         
        selectColor = new Color(0,0,0);
        fontColor = new Color(0,0,0);
        btnFont = new Font("Georgia",Font.BOLD,17);
        numTool =  1;
        strokeSize = 15;
         
        colorRect = new Rectangle(13, 35, 35, 35);      // Color button (invisible but "click-able")
          
        pencilRect = new Rectangle(53, 35, 80, 35);     // Tool buttons (invisible but "click-able")
        brushRect = new Rectangle(138, 35, 80, 35);
        sprayRect = new Rectangle(223, 35, 80, 35);
         
        sizeRect = new Rectangle(308, 35, 60, 35);      // Size buttons (invisible but "click-able")
         
        newRect = new Rectangle(373, 35, 70, 35);       // Layer buttons (invisible but "click-able")
        prevRect = new Rectangle(448, 35, 70, 35);
        nextRect = new Rectangle(523, 35, 70, 35);
        delRect = new Rectangle(598, 35, 70, 35);
         
        menuRect = new Rectangle(8,30,paintLab.getAppWidth(), paintLab.getMenuHeight());
    }
     
    public void createMenu() {  // Creates the physical appearance of the menu
        menuImage = new BufferedImage(paintLab.getAppWidth(),paintLab.getMenuHeight(),BufferedImage.TYPE_INT_ARGB);
        g2Menu = (Graphics2D) menuImage.getGraphics();
         
        makeBackgroundMenu();
        makeColorButton();
        makeSizeButton();
        makeToolButtons();
        makeLayerButtons();
    }
     
    private void makeBackgroundMenu() {     // Makes background menu
        menuColor = new Color(71,119,166);
        g2Menu.setColor(menuColor); 
        g2Menu.fillRect(0,0,paintLab.getAppWidth(), paintLab.getMenuHeight());
    }
     
    private void makeColorButton() {        // Makes visible color buttons
        paintColor = new Color(121,219,209);
        g2Menu.setColor(paintColor);
        g2Menu.fillRect(13, 35, 35, 35);
         
        g2Menu.setStroke(new BasicStroke(2, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND));
        g2Menu.setColor(selectColor);
        g2Menu.drawRect(13, 35, 35, 35);    // Draws border
    }
     
    private void makeToolButtons() {        // Makes visible tool buttons
        toolColor = new Color(240,236,189);
        g2Menu.setColor(toolColor);
         
        g2Menu.fillRoundRect(53, 35, 80, 35, 20, 20);       // 1st Tool Button: Pencil
        g2Menu.fillRoundRect(138, 35, 80, 35, 20, 20);      // 2nd Tool Button: Elastic
        g2Menu.fillRoundRect(223, 35, 80, 35, 20, 20);      // 3rd Tool Button: Paint Bucket
         
        g2Menu.setColor(fontColor);
        g2Menu.setFont(btnFont);
         
        g2Menu.drawString("PENCIL", 58, 58);                // Tool button labels
        g2Menu.drawString("BRUSH", 146, 58);
        g2Menu.drawString("SPRAY", 231, 58);
         
        g2Menu.setStroke(new BasicStroke(2, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND));
        g2Menu.setColor(selectColor);
        g2Menu.drawRoundRect(53, 35, 80, 35, 20, 20);       // Draws border around first selected tool which is the pencil tool
         
        g2Menu.setColor(menuColor);
        g2Menu.drawRoundRect(138, 35, 80, 35, 20, 20);      // Draws "invisible" border (same color as the menu) on the other two tools
        g2Menu.drawRoundRect(223, 35, 80, 35, 20, 20); 
    }
 
    private void makeSizeButton() {     // Makes visible size buttons
        sizeColor = new Color(253, 191, 125);
         
        g2Menu.setColor(sizeColor);
        g2Menu.fillRoundRect(308, 35, 60, 35, 20, 20);
         
        g2Menu.setStroke(new BasicStroke(2, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND));
        g2Menu.setColor(selectColor);
        g2Menu.drawRoundRect(308, 35, 60, 35, 20, 20);      // Draws border
         
        g2Menu.setColor(fontColor);
        g2Menu.setFont(btnFont);
         
        g2Menu.drawString(strokeSize + " px", 314, 56);
    }
     
    private void makeLayerButtons() {   // Makes visible layer buttons
        layerBtnColor = new Color(119,237,156);
        g2Menu.setColor(layerBtnColor);
         
        g2Menu.fillRoundRect(373, 35, 70, 35, 20, 20);      // Layer Button: New Layer
        g2Menu.fillRoundRect(448, 35, 70, 35, 20, 20);      // Layer Button: Previous Layer
        g2Menu.fillRoundRect(523, 35, 70, 35, 20, 20);      // Layer Button: Next Layer
        g2Menu.fillRoundRect(598, 35, 70, 35, 20, 20);      // Layer Button: Delete Layer
         
        g2Menu.fillRoundRect(673, 35, 75, 35, 20, 20);      // Layer Button: Number of current layer
         
        g2Menu.setColor(fontColor);
        g2Menu.setFont(btnFont);
         
        g2Menu.drawString("NEW", 383, 58);                  // Layer button labels
        g2Menu.drawString("PREV.", 457, 58);
        g2Menu.drawString("NEXT", 533, 58);
        g2Menu.drawString("DEL.", 613, 58);
         
        g2Menu.setFont(new Font("Georgia",Font.PLAIN,19));
        g2Menu.drawString("1 of 1", 683, 56);               // Initial layer status (when the paint program begins)
    }
     
    public void select(int x, int y) {      // Selects menu items
        if (colorRect.contains(x, y)) {     // If mouse clicks on the color button
            selectColor();
        }
                         
        if (pencilRect.contains(x, y)) {    // If mouse selects a tool
            numTool = 1;
        }
        else if (brushRect.contains(x, y)) {
            numTool = 2;
        }
        else if (sprayRect.contains(x, y)) {
            numTool = 3;
        }
         
        if (sizeRect.contains(x, y)) {      // If mouse clicks on the size button
            selectSize();
        }
         
        if (newRect.contains(x, y)) {       // If mouse clicks on a layer button
            layersArray.newLayer();
        }
        else if (prevRect.contains(x, y)) {
            layersArray.previousLayer();
        }
        else if (nextRect.contains(x, y)) {
            layersArray.nextLayer();
        }
        else if (delRect.contains(x, y)) {
            layersArray.deleteLayer();
        }
    }
     
    private void selectColor() {    // When the user selects on the color button
        JColorChooser chooser = new JColorChooser();        // Color panel
        AbstractColorChooserPanel[] panels = chooser.getChooserPanels();
          
        for (AbstractColorChooserPanel accp : panels) {     // Only the RGB tab will show
            if (accp.getDisplayName().equals("RGB")) {
                JOptionPane.showMessageDialog(null, accp);
            }
        }
         
        paintLab.repaint(); // Repaints the window in case some of the graphics disappeared due to the color chooser popping up
        Color pColor = chooser.getColor();
         
        // Since transparent lines are compounded on top of each other when draw in succession causing the transparent lines to appear opaque
        // I converted the RGBA values into RGB.
        // Now, the color of the menu's color button matches the color drawn on the canvas
        int r, g, b;
        r = pColor.getRed() * pColor.getAlpha() + pColor.getRed() * (255 - pColor.getAlpha());
        g = pColor.getGreen() * pColor.getAlpha() + pColor.getGreen() * (255 - pColor.getAlpha());
        b = pColor.getBlue() * pColor.getAlpha() + pColor.getBlue() * (255 - pColor.getAlpha());
        paintColor = new Color(r / 255, g / 255, b / 255);
         
        changeColorMenu();
    }
     
    private void selectSize() {     // When the user selects on the size button
        do {
            stringStroke = JOptionPane.showInputDialog("How many pixels wide should your paint tools be?");
 
            if (stringStroke == null) { // If the user exits the dialog box
                JOptionPane.showMessageDialog(null,"Since you did not provide a value for the width of the paint tool, the width is set to 15 px.");
                strokeSize = 15;
            }
            else {
                strokeSize = Integer.parseInt(stringStroke);
                 
                if (strokeSize < 0)     // If the user enters a negative value
                    JOptionPane.showMessageDialog(null,"You cannot have a tool with a width less than 0. Please try again with a higher value.");
                else if (strokeSize > paintLab.getCanvasHeight())   // If the user enters an extremely large value
                    JOptionPane.showMessageDialog(null,"Using a tool with a width greater than the canvas height (" + paintLab.getCanvasHeight() + " px) is unnecesarily large. Please try again with a lower value.");
            }
        } while ((strokeSize < 0) || (strokeSize > paintLab.getCanvasHeight()));    // Loop will continue until the user provides a positive number smaller than the canvas height
        changeSizeMenu();
    }
     
    private void changeColorMenu() {    // Changes the appearance of the color button after it is clicked
        g2Menu.setColor(Color.white);   // Erases previous button
        g2Menu.fillRect(13, 35, 35, 35);
         
        g2Menu.setColor(paintColor);    // Paints button with new color
        g2Menu.fillRect(13, 35, 35, 35);
         
        g2Menu.setStroke(new BasicStroke(2, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND));
        g2Menu.setColor(selectColor);   // Redraws border
        g2Menu.drawRect(13, 35, 35, 35);
    }
     
    public void changeToolMenu() {      // Changes the appearance of the tool button after it is clicked
        g2Menu.setStroke(new BasicStroke(2, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND));
        g2Menu.setColor(selectColor);
         
        if (numTool == 1)   // Draws border around selected tool box
            g2Menu.drawRoundRect(53, 35, 80, 35, 20, 20);
        else if (numTool == 2)
            g2Menu.drawRoundRect(138, 35, 80, 35, 20, 20);
        else if (numTool == 3)
            g2Menu.drawRoundRect(223, 35, 80, 35, 20, 20);
         
        g2Menu.setColor(menuColor);
         
        // "If" statements instead of "else if", because multiple boxes are not selected at a time
        if (numTool != 1)       // Erases border around previously selected tool box by drawing that same rectangle with the menu color
            g2Menu.drawRoundRect(53, 35, 80, 35, 20, 20);
        if (numTool != 2)
            g2Menu.drawRoundRect(138, 35, 80, 35, 20, 20);
        if (numTool != 3)
            g2Menu.drawRoundRect(223, 35, 80, 35, 20, 20);
    }
 
    private void changeSizeMenu() {     // Changes the appearance of the size button after it is clicked
        g2Menu.setColor(sizeColor);
        g2Menu.fillRoundRect(308, 35, 60, 35, 20, 20);  // Erases previous size by drawing button again
         
        g2Menu.setStroke(new BasicStroke(2, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND));
        g2Menu.setColor(selectColor);
        g2Menu.drawRoundRect(308, 35, 60, 35, 20, 20);  // Draws border
         
        g2Menu.setFont(btnFont);
        g2Menu.setColor(fontColor);
         
        // Depending on the number of digits of the size status, the status is printed with a certain size and location
        if (strokeSize <= 99) {
            g2Menu.drawString(strokeSize + " px", 314, 56);
        }
        else {
            g2Menu.setFont(new Font("Georgia",Font.BOLD,14));
            g2Menu.drawString(strokeSize + " px", 312, 56);
        }
    }
     
    public void changeLayerMenu() {     // Changes the appearance of the layers' status after the user has created or deleted a layer, or moved to a different layer
        g2Menu.setColor(layerBtnColor);
        g2Menu.fillRoundRect(673, 35, 75, 35, 20, 20);  // Redraw the layer status rectangle to cover the previous status
        g2Menu.setColor(fontColor);
         
        // Depending on the number of digits in the layer status, the status is printed with a certain size and location
        if ((layersArray.getcLayerNum() < 9) && (layersArray.getImageArray().size() < 10)) {
            g2Menu.setFont(new Font("Georgia",Font.PLAIN,19));
            g2Menu.drawString(String.valueOf(layersArray.getcLayerNum() + 1) + " of " + String.valueOf(layersArray.getImageArray().size()), 683, 56);
        }
        else {
            g2Menu.setFont(new Font("Georgia",Font.PLAIN,17));
            g2Menu.drawString(String.valueOf(layersArray.getcLayerNum() + 1) + " of " + String.valueOf(layersArray.getImageArray().size()), 683, 56);
        }
    }
     
    public int getNumTool() {       // Returns private numTool attribute
        return numTool;
    }
     
    public int getStrokeSize() {    // Returns private numStrokeSize attribute
        return strokeSize;
    }
}
 
						/*****************************************************************************
						*   Lab13: The Paint Program: Menu class
						*   Name: Samantha Yu
						*   Submitted to AP Computer Science 12: January 27, 2015
						*   Last modified: April 4, 2015
						*   Teacher: Christopher Slowley
						******************************************************************************/

						import java.awt.BasicStroke;
						import java.awt.Color;
						import java.awt.Font;
						import java.awt.Graphics2D;
						import java.awt.Rectangle;
						import java.awt.image.BufferedImage;
						import javax.swing.JColorChooser;
						import javax.swing.JOptionPane;
						import javax.swing.colorchooser.AbstractColorChooserPanel;

						public class Menu {
							// Menu buttons are not physically seen, but are the "click-able" items
							Rectangle menuRect, colorRect, sizeRect;
							Rectangle pencilRect, brushRect, sprayRect;
							Rectangle newRect, prevRect, nextRect, delRect;
							
							// Menu appearance
							Color menuColor, paintColor, toolColor, sizeColor, fontColor, layerBtnColor, selectColor;
							Font btnFont;
							private int numTool, strokeSize;
							
							String stringStroke;        // Used when getting the color from dialog boxes
							
							BufferedImage menuImage;    // To prevent the menu bar being erased when the window is no longer active, menuImage will store the current image
							Graphics2D g2Menu;          // g2Menu will paint graphics onto that image when necessary
							
							PaintApp paintLab;  // A PaintApp object has only one instance and allows this class to access its non-static methods and attributes
								// I did not make PaintApp's attributes static, because those attributes refer to the one and same object, the paint program
								// I did not want to place the Menu's methods into PaintApp, because those methods belong in its own separate category
								// So, this Menu class must be able to access the dimensions of PaintApp such as the get methods like getAppWidth()
							Layers layersArray;
							
							public Menu(PaintApp pLab, Layers lArray) { // When the menu object is constructed, the invisible but "click-able" buttons are also constructed
								paintLab = pLab;        // Initializes paintLab to the actual PaintApp, and layersArray to the actual layers array
								layersArray = lArray;   // So that Menu class' methods can access the non-static fields and methods, such as imageArray
								
								selectColor = new Color(0,0,0);
								fontColor = new Color(0,0,0);
								btnFont = new Font("Georgia",Font.BOLD,17);
								numTool =  1;
								strokeSize = 15;
								
								colorRect = new Rectangle(13, 35, 35, 35);      // Color button (invisible but "click-able")
								 
								pencilRect = new Rectangle(53, 35, 80, 35);     // Tool buttons (invisible but "click-able")
								brushRect = new Rectangle(138, 35, 80, 35);
								sprayRect = new Rectangle(223, 35, 80, 35);
								
								sizeRect = new Rectangle(308, 35, 60, 35);      // Size buttons (invisible but "click-able")
								
								newRect = new Rectangle(373, 35, 70, 35);       // Layer buttons (invisible but "click-able")
								prevRect = new Rectangle(448, 35, 70, 35);
								nextRect = new Rectangle(523, 35, 70, 35);
								delRect = new Rectangle(598, 35, 70, 35);
								
								menuRect = new Rectangle(8,30,paintLab.getAppWidth(), paintLab.getMenuHeight());
							}
							
							public void createMenu() {  // Creates the physical appearance of the menu
								menuImage = new BufferedImage(paintLab.getAppWidth(),paintLab.getMenuHeight(),BufferedImage.TYPE_INT_ARGB);
								g2Menu = (Graphics2D) menuImage.getGraphics();
								
								makeBackgroundMenu();
								makeColorButton();
								makeSizeButton();
								makeToolButtons();
								makeLayerButtons();
							}
							
							private void makeBackgroundMenu() {     // Makes background menu
								menuColor = new Color(71,119,166);
								g2Menu.setColor(menuColor);  
								g2Menu.fillRect(0,0,paintLab.getAppWidth(), paintLab.getMenuHeight()); 
							}
							
							private void makeColorButton() {        // Makes visible color buttons
								paintColor = new Color(121,219,209);
								g2Menu.setColor(paintColor);
								g2Menu.fillRect(13, 35, 35, 35);
								
								g2Menu.setStroke(new BasicStroke(2, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND));
								g2Menu.setColor(selectColor);
								g2Menu.drawRect(13, 35, 35, 35);    // Draws border
							}
							
							private void makeToolButtons() {        // Makes visible tool buttons
								toolColor = new Color(240,236,189);
								g2Menu.setColor(toolColor);
								
								g2Menu.fillRoundRect(53, 35, 80, 35, 20, 20);       // 1st Tool Button: Pencil
								g2Menu.fillRoundRect(138, 35, 80, 35, 20, 20);      // 2nd Tool Button: Elastic
								g2Menu.fillRoundRect(223, 35, 80, 35, 20, 20);      // 3rd Tool Button: Paint Bucket
								
								g2Menu.setColor(fontColor);
								g2Menu.setFont(btnFont);
								
								g2Menu.drawString("PENCIL", 58, 58);                // Tool button labels
								g2Menu.drawString("BRUSH", 146, 58);
								g2Menu.drawString("SPRAY", 231, 58);
								
								g2Menu.setStroke(new BasicStroke(2, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND));
								g2Menu.setColor(selectColor);
								g2Menu.drawRoundRect(53, 35, 80, 35, 20, 20);       // Draws border around first selected tool which is the pencil tool
								
								g2Menu.setColor(menuColor);
								g2Menu.drawRoundRect(138, 35, 80, 35, 20, 20);      // Draws "invisible" border (same color as the menu) on the other two tools
								g2Menu.drawRoundRect(223, 35, 80, 35, 20, 20);  
							}

							private void makeSizeButton() {     // Makes visible size buttons
								sizeColor = new Color(253, 191, 125);
								
								g2Menu.setColor(sizeColor);
								g2Menu.fillRoundRect(308, 35, 60, 35, 20, 20);
								
								g2Menu.setStroke(new BasicStroke(2, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND));
								g2Menu.setColor(selectColor);
								g2Menu.drawRoundRect(308, 35, 60, 35, 20, 20);      // Draws border
								
								g2Menu.setColor(fontColor);
								g2Menu.setFont(btnFont);
								
								g2Menu.drawString(strokeSize + " px", 314, 56);
							}
							
							private void makeLayerButtons() {   // Makes visible layer buttons
								layerBtnColor = new Color(119,237,156);
								g2Menu.setColor(layerBtnColor);
								
								g2Menu.fillRoundRect(373, 35, 70, 35, 20, 20);      // Layer Button: New Layer
								g2Menu.fillRoundRect(448, 35, 70, 35, 20, 20);      // Layer Button: Previous Layer
								g2Menu.fillRoundRect(523, 35, 70, 35, 20, 20);      // Layer Button: Next Layer
								g2Menu.fillRoundRect(598, 35, 70, 35, 20, 20);      // Layer Button: Delete Layer
								
								g2Menu.fillRoundRect(673, 35, 75, 35, 20, 20);      // Layer Button: Number of current layer
								
								g2Menu.setColor(fontColor);
								g2Menu.setFont(btnFont);
								
								g2Menu.drawString("NEW", 383, 58);                  // Layer button labels
								g2Menu.drawString("PREV.", 457, 58);
								g2Menu.drawString("NEXT", 533, 58);
								g2Menu.drawString("DEL.", 613, 58);
								
								g2Menu.setFont(new Font("Georgia",Font.PLAIN,19));
								g2Menu.drawString("1 of 1", 683, 56);               // Initial layer status (when the paint program begins)
							}
							
							public void select(int x, int y) {      // Selects menu items
								if (colorRect.contains(x, y)) {     // If mouse clicks on the color button
									selectColor();
								}
												
								if (pencilRect.contains(x, y)) {    // If mouse selects a tool
									numTool = 1;
								}
								else if (brushRect.contains(x, y)) {
									numTool = 2;
								}
								else if (sprayRect.contains(x, y)) {
									numTool = 3;
								}
								
								if (sizeRect.contains(x, y)) {      // If mouse clicks on the size button
									selectSize();
								}
								
								if (newRect.contains(x, y)) {       // If mouse clicks on a layer button
									layersArray.newLayer();
								}
								else if (prevRect.contains(x, y)) {
									layersArray.previousLayer();
								}
								else if (nextRect.contains(x, y)) {
									layersArray.nextLayer();
								}
								else if (delRect.contains(x, y)) {
									layersArray.deleteLayer();
								}
							}
							
							private void selectColor() {    // When the user selects on the color button
								JColorChooser chooser = new JColorChooser();        // Color panel
								AbstractColorChooserPanel[] panels = chooser.getChooserPanels();
								 
								for (AbstractColorChooserPanel accp : panels) {     // Only the RGB tab will show
									if (accp.getDisplayName().equals("RGB")) {
										JOptionPane.showMessageDialog(null, accp);
									}
								}
								
								paintLab.repaint(); // Repaints the window in case some of the graphics disappeared due to the color chooser popping up
								Color pColor = chooser.getColor();
								
								// Since transparent lines are compounded on top of each other when draw in succession causing the transparent lines to appear opaque
								// I converted the RGBA values into RGB.
								// Now, the color of the menu's color button matches the color drawn on the canvas
								int r, g, b;
								r = pColor.getRed() * pColor.getAlpha() + pColor.getRed() * (255 - pColor.getAlpha());
								g = pColor.getGreen() * pColor.getAlpha() + pColor.getGreen() * (255 - pColor.getAlpha());
								b = pColor.getBlue() * pColor.getAlpha() + pColor.getBlue() * (255 - pColor.getAlpha());
								paintColor = new Color(r / 255, g / 255, b / 255);
								
								changeColorMenu();
							}
							
							private void selectSize() {     // When the user selects on the size button
								do {
									stringStroke = JOptionPane.showInputDialog("How many pixels wide should your paint tools be?");

									if (stringStroke == null) { // If the user exits the dialog box
										JOptionPane.showMessageDialog(null,"Since you did not provide a value for the width of the paint tool, the width is set to 15 px.");
										strokeSize = 15;
									}
									else {
										strokeSize = Integer.parseInt(stringStroke);
										
										if (strokeSize < 0)     // If the user enters a negative value
											JOptionPane.showMessageDialog(null,"You cannot have a tool with a width less than 0. Please try again with a higher value.");
										else if (strokeSize > paintLab.getCanvasHeight())   // If the user enters an extremely large value
											JOptionPane.showMessageDialog(null,"Using a tool with a width greater than the canvas height (" + paintLab.getCanvasHeight() + " px) is unnecesarily large. Please try again with a lower value.");
									}
								} while ((strokeSize < 0) || (strokeSize > paintLab.getCanvasHeight()));    // Loop will continue until the user provides a positive number smaller than the canvas height
								changeSizeMenu();
							}
							
							private void changeColorMenu() {    // Changes the appearance of the color button after it is clicked
								g2Menu.setColor(Color.white);   // Erases previous button
								g2Menu.fillRect(13, 35, 35, 35);
								
								g2Menu.setColor(paintColor);    // Paints button with new color
								g2Menu.fillRect(13, 35, 35, 35);
								
								g2Menu.setStroke(new BasicStroke(2, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND));
								g2Menu.setColor(selectColor);   // Redraws border
								g2Menu.drawRect(13, 35, 35, 35);
							}
							
							public void changeToolMenu() {      // Changes the appearance of the tool button after it is clicked
								g2Menu.setStroke(new BasicStroke(2, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND));
								g2Menu.setColor(selectColor);
								
								if (numTool == 1)   // Draws border around selected tool box
									g2Menu.drawRoundRect(53, 35, 80, 35, 20, 20);
								else if (numTool == 2)
									g2Menu.drawRoundRect(138, 35, 80, 35, 20, 20);
								else if (numTool == 3)
									g2Menu.drawRoundRect(223, 35, 80, 35, 20, 20);
								
								g2Menu.setColor(menuColor);
								
								// "If" statements instead of "else if", because multiple boxes are not selected at a time
								if (numTool != 1)       // Erases border around previously selected tool box by drawing that same rectangle with the menu color
									g2Menu.drawRoundRect(53, 35, 80, 35, 20, 20);
								if (numTool != 2)
									g2Menu.drawRoundRect(138, 35, 80, 35, 20, 20);
								if (numTool != 3)
									g2Menu.drawRoundRect(223, 35, 80, 35, 20, 20);
							}

							private void changeSizeMenu() {     // Changes the appearance of the size button after it is clicked
								g2Menu.setColor(sizeColor);
								g2Menu.fillRoundRect(308, 35, 60, 35, 20, 20);  // Erases previous size by drawing button again
								
								g2Menu.setStroke(new BasicStroke(2, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND));
								g2Menu.setColor(selectColor);
								g2Menu.drawRoundRect(308, 35, 60, 35, 20, 20);  // Draws border
								
								g2Menu.setFont(btnFont);
								g2Menu.setColor(fontColor);
								
								// Depending on the number of digits of the size status, the status is printed with a certain size and location
								if (strokeSize <= 99) {
									g2Menu.drawString(strokeSize + " px", 314, 56);
								}
								else {
									g2Menu.setFont(new Font("Georgia",Font.BOLD,14));
									g2Menu.drawString(strokeSize + " px", 312, 56);
								}
							}
							
							public void changeLayerMenu() {     // Changes the appearance of the layers' status after the user has created or deleted a layer, or moved to a different layer
								g2Menu.setColor(layerBtnColor);
								g2Menu.fillRoundRect(673, 35, 75, 35, 20, 20);  // Redraw the layer status rectangle to cover the previous status
								g2Menu.setColor(fontColor);
								
								// Depending on the number of digits in the layer status, the status is printed with a certain size and location
								if ((layersArray.getcLayerNum() < 9) && (layersArray.getImageArray().size() < 10)) {
									g2Menu.setFont(new Font("Georgia",Font.PLAIN,19));
									g2Menu.drawString(String.valueOf(layersArray.getcLayerNum() + 1) + " of " + String.valueOf(layersArray.getImageArray().size()), 683, 56);
								}
								else {
									g2Menu.setFont(new Font("Georgia",Font.PLAIN,17));
									g2Menu.drawString(String.valueOf(layersArray.getcLayerNum() + 1) + " of " + String.valueOf(layersArray.getImageArray().size()), 683, 56);
								}
							}
							
							public int getNumTool() {       // Returns private numTool attribute
								return numTool;
							}
							
							public int getStrokeSize() {    // Returns private numStrokeSize attribute
								return strokeSize;
							}
						}
						
Tools.java
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/*****************************************************************************
*   Lab13: The Paint Program: Tools class
*   Name: Samantha Yu
*   Submitted to AP Computer Science 12: January 27, 2015
*   Last modified: April 4, 2015
*   Teacher: Christopher Slowley
******************************************************************************/
 
import java.awt.BasicStroke;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.Random;
 
public class Tools {
    BufferedImage cvMem;    // "Current virtual memory"; off-screen image that the current layer which is painted upon
     
    Graphics2D cgBuffer;    // "Current graphics' buffer"; a graphics context for drawing an off-screen image; all cgBuffer information is sent to cvMem
        // The Graphics2D class provides "more sophisticated control over geometry, coordinate transformations, color management, and text layout"
        // (see "http://docs.oracle.com/javase/7/docs/api/java/awt/Graphics2D.html") than its superclass, the Graphics class,
        // Such as being able to paint lines that have different edges
     
    PaintApp paintLab;  // A PaintApp object has only one instance and allows this class to access its non-static methods and attributes
        // I did not make PaintApp's attributes static, because those attributes refer to the one and same object, the paint program
        // I did not want to place the Tools' methods into PaintApp, because those methods belong in its own separate category
        // So, this Tools class must be able to access the dimensions of PaintApp such as the get methods like getAppWidth()
    Menu menuBar;
    Layers layersArray;
    private int randX, randY;
     
    public Tools(PaintApp pLab, Menu menuB, Layers lArray) {        // A Tools object is constructed
        menuBar = menuB;    // Initializes menuBar to the actual menu, paintLab to the actual PaintApp, and layersArray to the actual layers array
        paintLab = pLab;    // So that Tools class' methods can access the non-static fields and methods, such as imageArray
        layersArray = lArray;
    }
     
    private void pencil(int oldX, int oldY, int newX, int newY) {   // Pencil tool with a rounded stroke
        cgBuffer.setStroke(new BasicStroke(menuBar.getStrokeSize(), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
        cgBuffer.drawLine(oldX,oldY,newX,newY);
    }
     
    private void brush(int oldX, int oldY, int newX, int newY) {    // Brush tool (has "missing" lines in its stroke at a curve)
        cgBuffer.setStroke(new BasicStroke(menuBar.getStrokeSize(), BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER));
        cgBuffer.drawLine(oldX,oldY,newX,newY);
    }
     
    private void spray(int newX, int newY) {    // Spray can tool
        Random rand = new Random();
         
        for (int k=1; k<2*menuBar.getStrokeSize(); k++) {   // Several tiny random circles are painted around the current coordinate
            randX = rand.nextInt(menuBar.getStrokeSize()) + newX - (int) menuBar.getStrokeSize()/2;
            randY = rand.nextInt(menuBar.getStrokeSize()) + newY - (int) menuBar.getStrokeSize()/2;
            cgBuffer.fillOval(randX, randY, (int) 3, (int) 3); 
        }
    }
     
    public void useTool(int numTool, int oldX, int oldY, int newX, int newY) {  // Selects specific tool
        cvMem = layersArray.getImageArray().get(layersArray.getcLayerNum());    // Grabs the current layer
        cgBuffer = (Graphics2D) cvMem.getGraphics();    // Creates the graphics context for that layer to be used in double buffering
        cgBuffer.setColor(menuBar.paintColor);
         
        // The y-coordinates are subtracted by the menu bar's height, because the canvas layers do not include the menu bar
        if (numTool == 1)
            pencil(oldX, oldY - paintLab.getMenuHeight(), newX, newY - paintLab.getMenuHeight());
        else if (numTool == 2)
            brush(oldX, oldY - paintLab.getMenuHeight(), newX, newY - paintLab.getMenuHeight());
        else if (numTool == 3)
            spray(newX, newY - paintLab.getMenuHeight());
    }
}
 
						/*****************************************************************************
						*   Lab13: The Paint Program: Tools class
						*   Name: Samantha Yu
						*   Submitted to AP Computer Science 12: January 27, 2015
						*   Last modified: April 4, 2015
						*   Teacher: Christopher Slowley
						******************************************************************************/

						import java.awt.BasicStroke;
						import java.awt.Graphics2D;
						import java.awt.image.BufferedImage;
						import java.util.Random;

						public class Tools {
							BufferedImage cvMem;    // "Current virtual memory"; off-screen image that the current layer which is painted upon
							
							Graphics2D cgBuffer;    // "Current graphics' buffer"; a graphics context for drawing an off-screen image; all cgBuffer information is sent to cvMem
								// The Graphics2D class provides "more sophisticated control over geometry, coordinate transformations, color management, and text layout"
								// (see "http://docs.oracle.com/javase/7/docs/api/java/awt/Graphics2D.html") than its superclass, the Graphics class,
								// Such as being able to paint lines that have different edges
							
							PaintApp paintLab;  // A PaintApp object has only one instance and allows this class to access its non-static methods and attributes
								// I did not make PaintApp's attributes static, because those attributes refer to the one and same object, the paint program
								// I did not want to place the Tools' methods into PaintApp, because those methods belong in its own separate category
								// So, this Tools class must be able to access the dimensions of PaintApp such as the get methods like getAppWidth()
							Menu menuBar;
							Layers layersArray;
							private int randX, randY;
							
							public Tools(PaintApp pLab, Menu menuB, Layers lArray) {        // A Tools object is constructed
								menuBar = menuB;    // Initializes menuBar to the actual menu, paintLab to the actual PaintApp, and layersArray to the actual layers array
								paintLab = pLab;    // So that Tools class' methods can access the non-static fields and methods, such as imageArray
								layersArray = lArray;
							}
							
							private void pencil(int oldX, int oldY, int newX, int newY) {   // Pencil tool with a rounded stroke
								cgBuffer.setStroke(new BasicStroke(menuBar.getStrokeSize(), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
								cgBuffer.drawLine(oldX,oldY,newX,newY);
							}
							
							private void brush(int oldX, int oldY, int newX, int newY) {    // Brush tool (has "missing" lines in its stroke at a curve)
								cgBuffer.setStroke(new BasicStroke(menuBar.getStrokeSize(), BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER)); 
								cgBuffer.drawLine(oldX,oldY,newX,newY);
							}
							
							private void spray(int newX, int newY) {    // Spray can tool
								Random rand = new Random();
								
								for (int k=1; k<2*menuBar.getStrokeSize(); k++) {   // Several tiny random circles are painted around the current coordinate
									randX = rand.nextInt(menuBar.getStrokeSize()) + newX - (int) menuBar.getStrokeSize()/2;
									randY = rand.nextInt(menuBar.getStrokeSize()) + newY - (int) menuBar.getStrokeSize()/2;
									cgBuffer.fillOval(randX, randY, (int) 3, (int) 3);  
								}
							}
							
							public void useTool(int numTool, int oldX, int oldY, int newX, int newY) {  // Selects specific tool
								cvMem = layersArray.getImageArray().get(layersArray.getcLayerNum());    // Grabs the current layer
								cgBuffer = (Graphics2D) cvMem.getGraphics();    // Creates the graphics context for that layer to be used in double buffering
								cgBuffer.setColor(menuBar.paintColor);
								
								// The y-coordinates are subtracted by the menu bar's height, because the canvas layers do not include the menu bar
								if (numTool == 1)
									pencil(oldX, oldY - paintLab.getMenuHeight(), newX, newY - paintLab.getMenuHeight());
								else if (numTool == 2)
									brush(oldX, oldY - paintLab.getMenuHeight(), newX, newY - paintLab.getMenuHeight());
								else if (numTool == 3)
									spray(newX, newY - paintLab.getMenuHeight());
							}
						}