Subscribe to Dogstopper's Code Mania        RSS Feed
-----

OpenOffice Draw Imitation

Icon Leave Comment
Have you ever wondered how to make a draw interface like the one you see in OpenOffice Draw? Today, I am going to show you not just how to draw with the mouse, but also how to make the panel that you draw on appear in the center of a screen (so that it looks like a sheet of paper). The only thing that I will not show you how to do today is to zoom in and out. We'll save that lesson for later.



So now! Let's see the code for the all-encompassing JFrame:
public class OpenOfficeFrame extends JFrame implements ActionListener {
	
	//------------------------------------------------ Custom Components ------
	private PageContainer pc;
	
	public OpenOfficeFrame() {
		//-------------------------------------------- Settings ---------------
		super("OpenOfficeDraw || Ver. 1.0");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setSize(1000, 600);
		if (Toolkit.getDefaultToolkit().
				isFrameStateSupported(JFrame.MAXIMIZED_BOTH)) {
			setExtendedState(JFrame.MAXIMIZED_BOTH);
		}
  
                //-------------------------------------------- Add Components ---------
		pc = new PageContainer();
		JScrollPane scroll = new JScrollPane(pc,
				ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
				ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		scroll.getVerticalScrollBar().setUnitIncrement(12);
		add(scroll, BorderLayout.CENTER);
	
		
		//-------------------------------------------- Look and Feel ----------
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (ClassNotFoundException e) {
		} catch (InstantiationException e) {
		} catch (IllegalAccessException e) {
		} catch (UnsupportedLookAndFeelException e) {
		}
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		JFrame frame = new MusicFrame();
		frame.setVisible(true);
	}
}



Well, in the settings area, we make the panel be 1000 x 600 if maximized is not allowed. This is also out default restored size.
if (Toolkit.getDefaultToolkit().
				isFrameStateSupported(JFrame.MAXIMIZED_BOTH)) {
			setExtendedState(JFrame.MAXIMIZED_BOTH);



In this segment, we check to see whether Maximized screen is supported, and if it is, expand it to maximized.

                //-------------------------------------------- Add Components ---------
		pc = new PageContainer();
		JScrollPane scroll = new JScrollPane(pc,
				ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
				ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		scroll.getVerticalScrollBar().setUnitIncrement(12);
		add(scroll, BorderLayout.CENTER);



In this segment, we add a PageContainer (we will write that part in a second) to a ScrollPane so that it can be scrolled like in OODraw. To speed up the vertical scrollbar, we make it jump by 12 lines. Then we add that unit to the JFrame - right in the center.

		//-------------------------------------------- Look and Feel ----------
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (ClassNotFoundException e) {
		} catch (InstantiationException e) {
		} catch (IllegalAccessException e) {
		} catch (UnsupportedLookAndFeelException e) {
		}



And finally, here, we set the Look and Feel to the default System feel (Like GTK for Linux, Windows theme for Windows, and Apple theme for Mac systems. If an exception is thrown, it will use the standard Java look and feel.



The next thing that we add is the PageContainer, which has a gradient background (my thing, not Open Office) and in the middle of it, there is a PagePanel, which represents the piece of paper onto which we draw. We also pad that paper with an empty banner that merely has a size.
public class PageContainer extends JPanel {

	PagePanel pp;
	
	public PageContainer() {
		
                // Make a JPanel so that we add the PagePanel to it and give it
                // padding by using a blank banner
		JComponent pane = new JPanel();
		pp = new PagePanel();
		pane.setBorder(BorderFactory.createEmptyBorder(20, 3, 20, 3));
		pane.setLayout(new BoxLayout(pane, BoxLayout.X_AXIS));
		pane.setOpaque(false);

		pane.add(pp);
		add(pane);
	}
	
        // In this method, all that happens is we make a Gradient from light blue
        // to dark Blue and paint it as the background. 
	@Override
	public void paintComponent(Graphics g) {
		Graphics2D g2d = (Graphics2D) g;
		GradientPaint gp = new GradientPaint(0,0, 
			    new Color(170,190,226),
			    getWidth(),getHeight(), 
			    new Color(86,126,177));  
		g2d.setPaint(gp);
		g2d.fillRect(0, 0, getWidth(), getHeight());
	}
}



So, in case you didn't understand, here it is in chucks:
JComponent pane = new JPanel();
		pp = new PagePanel();

		pane.setBorder(BorderFactory.createEmptyBorder(20, 3, 20, 3));
		pane.setLayout(new BoxLayout(pane, BoxLayout.X_AXIS));
		pane.setOpaque(false);

		pane.add(pp);
		add(pane);



First, we instantiate our JPanel and out "paper" panel.

The next three lines set an invisible border that is 20px on the top and bottom and 3px on both sides. This makes a padding around the "paper". Then we set it invisible, so that the background shows through.

Then the next 2 lines add the components to screen.

Next, we have the painting:
	@Override
	public void paintComponent(Graphics g) {
		Graphics2D g2d = (Graphics2D) g;
		GradientPaint gp = new GradientPaint(0,0, 
			    new Color(170,190,226),
			    getWidth(),getHeight(), 
			    new Color(86,126,177));  
		g2d.setPaint(gp);
		g2d.fillRect(0, 0, getWidth(), getHeight());
	}



This is actually really simple! First, we make Graphics into Graphics2D so that we can use a Gradient. On the next line, we make a new GradientPaint, specifying that it begins in the top, left corner with a light blue and the gradient should be finished by the time it hits the bottom-right edge of the panel. The next line sets that as the paint scheme and the next line paints a rectangle (the background) using that new GradientPaint scheme.



Finally, let's look at the PageComponent (or the "paper" if you will):
public class PagePanel extends JPanel implements MouseListener, MouseMotionListener {
	//------------------------------------------------ Constants --------------
	public static int WIDTH = 850;
	public static int HEIGHT = 1100;
	
	//------------------------------------------------ Graphics arrays --------
	private Vector<Point2D> points = new Vector<Point2D>();
	private Vector<Line2D> lines = new Vector<Line2D>();
	
	//------------------------------------------------ Manipulates the size ---
	private Dimension startingSize;

	public PagePanel() {
		super(new FlowLayout());
		
		// Set preferredSize and size
		startingSize = new Dimension(WIDTH, HEIGHT);
		setPreferredSize(startingSize);
		setSize(startingSize);
		
		// This makes a 2px black border
		setBorder(BorderFactory.createLineBorder(Color.black, 2));
		
		// Sets the Mouse events
		addMouseListener(this);
		addMouseMotionListener(this);
		
		// Repaint the screen every 16 milliseconds
		Timer time = new Timer(16, new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				repaint();				
			}
			
		});
		time.start();
	}
	
	// Need paint() here, not paintComponent() or the border will break.
	public void paint(Graphics g) {
		super.paint(g);
		Graphics2D g2d = (Graphics2D)g;
		
		g2d.setColor(Color.red);
		g2d.setStroke(new BasicStroke(10, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
		for (Line2D p : lines)
			g2d.drawLine((int)Math.round(p.getX1()), (int)Math.round(p.getY1()), 
				(int)Math.round(p.getX2()), (int)Math.round(p.getY2()));
		
	}

	// For every two points, make a line to paint. This smooths it out...
	@Override
	public void mouseDragged(MouseEvent e) {
		Point2D p = new Point2D.Double(e.getX(), e.getY());
		points.add(p);
		if (points.size() > 1)
			lines.add(new Line2D.Double(points.get(points.size()-2), points.get(points.size()-1)));
	}
	
	// Clear the points so that you can start painting "fresh"
	@Override
	public void mouseReleased(MouseEvent e) {
		points.clear();
	}
	
	//------------------------------------------------ Not used ---------------
	@Override
	public void mouseMoved(MouseEvent e) {}
	@Override
	public void mouseClicked(MouseEvent e) {}
	@Override
	public void mouseEntered(MouseEvent e) {}
	@Override
	public void mouseExited(MouseEvent e) {}
	@Override
	public void mousePressed(MouseEvent e) {}
	
}


Alright, I'm going to a little faster here; ready?
public PagePanel() {
		super(new FlowLayout());
		
		// Set preferredSize and size
		startingSize = new Dimension(WIDTH, HEIGHT);
		setPreferredSize(startingSize);
		setSize(startingSize);
		
		// This makes a 2px black border
		setBorder(BorderFactory.createLineBorder(Color.black, 2));
		
		// Sets the Mouse events
		addMouseListener(this);
		addMouseMotionListener(this);
		
		// Repaint the screen every 16 milliseconds
		Timer time = new Timer(16, new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				repaint();				
			}
			
		});
		time.start();
	}



The constructor sets the size, adds a small border around it, and then sets a timer to repaint the screen ever 16 milliseconds!

Ok! Moving on:
public void paint(Graphics g) {
		super.paint(g);
		Graphics2D g2d = (Graphics2D)g;
		
		g2d.setColor(Color.red);
		g2d.setStroke(new BasicStroke(10, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
		for (Line2D p : lines)
			g2d.drawLine((int)Math.round(p.getX1()), (int)Math.round(p.getY1()), 
				(int)Math.round(p.getX2()), (int)Math.round(p.getY2()));
		
	}



Notice that we use paint(), not paintComponent() here to avoid messing up the borders (don't believe me? Change the paint() and super.paint() to paintComponent() and super.paintComponent()). This has to do with the way Swing calls the sub-paint methods...

However, inside the method, we set the Graphics color to red and set the paintbrush to be 10 px wide and have a round cap and shape appearance. Then, for each line in the vector, paint the line.

Alright, let's look at the two methods that handle making those lines happen:
@Override
	public void mouseDragged(MouseEvent e) {
		Point2D p = new Point2D.Double(e.getX(), e.getY());
		points.add(p);
		if (points.size() > 1)
			lines.add(new Line2D.Double(points.get(points.size()-2), points.get(points.size()-1)));
	}
	
	// Clear the points so that you can start painting "fresh"
	@Override
	public void mouseReleased(MouseEvent e) {
		points.clear();
	}
	



In mouseDragged(), we add a new point and if then we make a new line between each point (since it is mouseDragged(), the lines are very small, we only use lines so that there are no gaps if the mouse is moving really quickly). However, in mouseReleased(), we clear the points so that they will not connect the next time you draw a shape. (If you don't understand, try commenting it out to see what happens when you draw two different shape).

Finally, we have the following methods to satisfy the interfaces:
@Override
	public void mouseMoved(MouseEvent e) {}

	@Override
	public void mouseClicked(MouseEvent e) {}
	@Override
	public void mouseEntered(MouseEvent e) {}
	@Override
	public void mouseExited(MouseEvent e) {}
	@Override
	public void mousePressed(MouseEvent e) {}





Obviously, this program is not NEARLY close to being complete, but there's a simple idea on how to float it in the center and make it appear like a piece of paper that you draw on, not just the whole screen...

See you next time here in Dogstopper's Code Mania! Thanks for reading.

0 Comments On This Entry

 

September 2014

S M T W T F S
 123456
78910111213
14151617181920
21 222324252627
282930    

Recent Entries

Search My Blog

Recent Comments

0 user(s) viewing

0 Guests
0 member(s)
0 anonymous member(s)