Creating the Painter Window


Painter has three menus: File, Draw, and Effects. As you'd expect, the File menu is used to create a new image, load an existing image, and save an image; here are the items in the File menu:

  • New

  • Open

  • Save As

  • Exit

The Draw and Effects menus work together; you select a drawing tool from the Draw menu (such as the line-drawing tool), and you can also select various effects from the Effects menu (such as drop shadows, texture fill, and so on). The items you select in these menus will have a check mark displayed in front of them, and they will stay checked until you select another item that is incompatible (such as selecting the ellipse-drawing tool). Here are the items in the Draw menu:

  • Draw lines

  • Draw rectangles

  • Draw rounded rectangles

  • Draw freehand

  • Draw text

And here are the possible effects you can use with the drawing tools from the Effects menu:

  • Plain

  • Solid fill

  • Gradient fill

  • Texture fill

  • Transparent

  • Draw thick lines

  • Drop shadow

  • Select color…

As before, the window the application draws is created in the application's constructor. And like the previous applications so far, this one is based on the Frame class. Here's what Painter's main method and constructor look like; the constructor adds the mouse listeners the application needs for drawing operations, and it constructs the menu system:

 public static void main(String[] args) {     new Painter(); } public Painter() {     setLayout(null);     addMouseListener(this);     addMouseMotionListener(this);     menubar = new MenuBar();     menu1 = new Menu("File");     menu2 = new Menu("Draw");     menu3 = new Menu("Effects");     newMenuItem = new MenuItem("New");     menu1.add(newMenuItem);     newMenuItem.addActionListener(this);     openMenuItem = new MenuItem("Open...");     menu1.add(openMenuItem);     openMenuItem.addActionListener(this);     saveMenuItem = new MenuItem("Save As...");     menu1.add(saveMenuItem);     saveMenuItem.addActionListener(this);     colorMenuItem = new MenuItem("Select color...");     menu3.add(colorMenuItem);     colorMenuItem.addActionListener(this);     exitMenuItem = new MenuItem("Exit");     menu1.add(exitMenuItem);     exitMenuItem.addActionListener(this);     .     .     . 

The Painter application has many check box menu items, where a menu item stays checked until it's unchecked by the user or the code. Here is how those items are created in the constructor:

 linesMenuItem = new CheckboxMenuItem("Draw lines"); menu2.add(linesMenuItem); linesMenuItem.addItemListener(this); ellipsesMenuItem = new CheckboxMenuItem("Draw ellipses"); menu2.add(ellipsesMenuItem); ellipsesMenuItem.addItemListener(this); rectanglesMenuItem = new CheckboxMenuItem("Draw rectangles"); menu2.add(rectanglesMenuItem); rectanglesMenuItem.addItemListener(this); roundedMenuItem = new CheckboxMenuItem(     "Draw rounded rectangles"); menu2.add(roundedMenuItem); roundedMenuItem.addItemListener(this); freehandMenuItem = new CheckboxMenuItem("Draw freehand"); menu2.add(freehandMenuItem); freehandMenuItem.addItemListener(this); plainMenuItem = new CheckboxMenuItem("Plain"); menu3.add(plainMenuItem); plainMenuItem.addItemListener(this); solidMenuItem = new CheckboxMenuItem("Solid fill"); menu3.add(solidMenuItem); solidMenuItem.addItemListener(this); gradientMenuItem = new CheckboxMenuItem("Gradient fill"); menu3.add(gradientMenuItem); gradientMenuItem.addItemListener(this); textureMenuItem = new CheckboxMenuItem("Texture fill"); menu3.add(textureMenuItem); textureMenuItem.addItemListener(this); transparentMenuItem = new CheckboxMenuItem("Transparent"); menu3.add(transparentMenuItem); transparentMenuItem.addItemListener(this); textMenuItem = new CheckboxMenuItem("Draw text"); menu2.add(textMenuItem); textMenuItem.addItemListener(this); thickMenuItem = new CheckboxMenuItem("Draw thick lines"); menu3.add(thickMenuItem); thickMenuItem.addItemListener(this); shadowMenuItem = new CheckboxMenuItem("Drop shadow"); menu3.add(shadowMenuItem); shadowMenuItem.addItemListener(this); menubar.add(menu1); menubar.add(menu2); menubar.add(menu3); setMenuBar(menubar); . . . 

Check box menu items are supported by the CheckboxMenuItem class, and you can see the significant methods of this class in Table 4.1.

Table 4.1. The Significant Methods of the java.awt.CheckboxMenuItem Class

Method

Does This

void addItemListener(ItemListener l)

Adds an item listener, which will get events from this check box menu item

ItemListener[] getItemListeners()

Returns an array containing all the item listeners connected to this check box menu item

Object[] getSelectedObjects()

Returns an array that contains the check box menu item label if the check box is selected, or returns null if the check box is not selected

boolean getState()

Returns true if this check box menu item is selected; returns false otherwise

void removeItemListener(ItemListener l)

Removes the given item listener, which means it will no longer get item events from this check box menu item

void setState(boolean b)

Sets the state of the check box menu item to the given state


REAL-WORLD SCENARIO

Centralizing the Code

As you can see, there are plenty of drawing tools and effects in the Painter program, and combining them in different ways gives the user many different options. Spreading the drawing logic throughout the entire program would quickly become unwieldy. When applications start to get bigger (Painter.java is about 16 pages of single-spaced code, for example), it gets harder to maintain and debug applications that have related functionality spread throughout.

For that reason, and as you may already know, developers usually try to centralize their code by function as much as possible when that code gets longer. For example, you could implement drawing operations and graphics effects throughout the code, including in the code that handles the menu items the user chooses to select a drawing tool or a graphics effect. Developers usually find, however, that it really pays to give some thought to what's going on beforehand and to concentrate related code in the same place as much as possible. This helps to avoid "spaghetti" code spread here and there throughout the application.

Accordingly, Painter was planned from the top down to handle this problem: The items in the Draw and Effects menus only set Boolean flags, such as ellipse when the user wants to draw ellipses, and shadow when the user wants to create drop shadows. These flags are used to communicate with the rest of the application.

The drawing operations themselves are centralized in the paint method, which simply has to check where the mouse is, check which drawing flags are set, and take it from there.

In this way, all the drawing operations and graphics effects take place in one area of the Painter program. Handling the menu items is super easyall you have to do is to set Boolean flags, which will be checked later when it's time to actually draw something. Now that the real action is corralled in the paint method, you know what code to work with when there's a problem.


Painter will also need a File dialog box for loading and saving images, so the constructor creates a new FileDialog object. Because the ImageIO class works with BufferedImage objects, the constructor also creates a buffered image and then displays the main window. Internally, the Painter application uses an Image object, named image, to do the drawing in. And there's also a dialog box of the same class put together for the Slapshot! application, OKCancelDialog, that Painter will use to read the text it's supposed to draw in the image. Here's how all this is set up in the constructor:

 dialog = new FileDialog(this, "File Dialog"); bufferedImage = new BufferedImage (imageWidth, imageHeight,    BufferedImage.TYPE_INT_BGR ); setSize(400, 400); setTitle("Painter"); setVisible(true); image = createImage(imageWidth, imageHeight); textDialog = new OkCancelDialog(this, "Enter your text", true); . . . 

All that's left in the constructor is to read in the image, named tile.jpg, used for filling images with text. You can use any image file for texture fills, as long as you name it tile.jpg; this application comes with a default tile.jpg file. Finally, the constructor adds the window closer as usual:

      try{         File inputFile = new File("tile.jpg");         tileImage = ImageIO.read(inputFile);      } catch (java.io.IOException ioe){          System.out.println("Need tile.jpg.");          System.exit(0);      }      this.addWindowListener(new WindowAdapter(){          public void windowClosing(              WindowEvent e){                  System.exit(0);              }          }      ); } 

Painter works by setting flags corresponding to the drawing operation or graphics effect you want to use and then checking those flags when it's time to do the actual drawing.



    Java After Hours(c) 10 Projects You'll Never Do at Work
    Java After Hours: 10 Projects Youll Never Do at Work
    ISBN: 0672327473
    EAN: 2147483647
    Year: 2006
    Pages: 128

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net