Core Java Technologies Tech Tips Tips, Techniques, and Sample Code Welcome to the Core Java Technologies Tech Tips for December 23, 2003. Here you'll get tips on using core Java technologies and APIs, such as those in Java 2 Platform, Standard Edition (J2SE). These tips were developed using Java 2 SDK, Standard Edition, v 1.4. This issue of the Core Java Technologies Tech Tips is written by John Zukowski, president of JZ Ventures, Inc. (http://www.jzventures.com). You can view this issue of the Tech Tips on the Web at http://java.sun.com/developer/JDCTechTips/2003/tt1223.html. See the Subscribe/Unsubscribe note at the end of this newsletter to subscribe to Tech Tips that focus on technologies and products in other Java platforms. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PROGRAMMER CHALLENGE This issue presents a programmer challenge. The challenge examines several of the tips from 2003 and some from prior years. While this year's tips covered topics in many areas across the Java platform spectrum (from working with the Java 3D and JMX APIs-to-dynamic class unloading), the challenge focuses on those necessary to create a simple drawing program similar to PhotoShop, though certainly not as complete. Let's start by identifying some of the features of the drawing program. First, you need a frame for the graphical application. As discussed in the December 8, 2003 Tech Tip on multithreading in Swing (http://java.sun.com/developer/JDCTechTips/2003/tt1208.html#1), you need to show the frame in the event thread, not the main program's thread. Some readers have wondered if the show method of java.awt.Window is deprecated. It's not, but the show method of java.awt.Component one is deprecated. Second, while not an absolute requirement, it's a good idea to place in a resource bundle the text strings that you use for things such as menu labels. You can learn more about this in the May 21, 1998 Tech Tip on using resource bundles (http://java.sun.com/developer/TechTips/1998/tt0521.html#tip2). You don't have to provide bundles for multiple languages, but it is always good practice to at least put the necessary strings in a bundle so that the program can be internationalized. Next, the drawing program must present the following menus, and support the indicated actions: File Menu: New (clear the image) Save (save the image) Exit (exit the application) Edit Menu: Clear (clear a selected area) Rotate (rotate a selected area) Horizontal Flip (flip the selected area horizontally) Vertical Flip (flip the selected area vertically) Attributes Menu: Background (select the background when clearing the screen) Foreground (select the foreground for the drawing color) Stroke Size (select the width of the drawing line) Toolbar (Drawing Modes): Normal (draw as the mouse moves) Draw Line (draw a line) Draw Rect (draw a rectangle) Fill Rect (draw a filled rectangle) Draw Oval (draw an oval) Fill Oval (draw a filled oval) Select Area (select an area to perform an operation from the Edit menu) The toolbar operations should be mutually exclusive. A user of the application should be allowed to do only one of the operations at a time. For the more graphically inclined, you can place icons on the toolbar for the different operations. Sun provides a set of icons in the Java look and feel Graphics Repository (http://java.sun.com/developer/techDocs/hi/repository/). One possible solution to the challenge (one that you'll see at the end of this tip) comprises three classes. One class presents the GUI, the second provides the drawing canvas (for simplicity, the drawing canvas has a fixed size of 640 x 480), and the third is the resource bundle for the text strings. Because the toolbar operations in the drawing program are mutually exclusive, the drawing canvas in your solution should have a mode for each operation. The mode that's active determines which operation to perform. So, your solution should define a set of modes. You could be fancy and create an enumerated type. For simplicity, you can use a set of integers like this: public static final int MODE_NORMAL = 0; public static final int MODE_LINE = 1; public static final int MODE_DRAWRECT = 2; public static final int MODE_FILLRECT = 3; public static final int MODE_DRAWOVAL = 4; public static final int MODE_FILLOVAL = 5; public static final int MODE_SELECT = 6; See the August 7, 2001 Tech Tip "Using Enumerations in Java Programming" (http://java.sun.com/developer/JDCTechTips/2001/tt0807.html#tip2) for more information on working with enumerations. Look for a new enum keyword in J2SE 1.5 for native language support of enumerations. For operations such as drawing lines and shapes, the drawing should be done to a temporary buffer until the mouse is released (drawing to a temporary area is done in a different color). After the mouse is released, the operation becomes final. For this to work correctly, you need to double buffer all drawing operations. When a user selects the clear operation from the menu, your program will only need to clear the buffer to reset the drawing canvas. For normal operation, the program should draw by tracking the mouse movement. When the mouse is down, the program should draw a line from the last point to the next point dragged to. For the remaining menu operations, the drawing program should save the starting mouse down position, and actively draw into the selected area. With selection, this would simply be drawing a bounding rectangle, for others, this would draw a rectangle or oval, optionally filled. Notice that drawing color and stroke size are set from the menu. You should use a JColorChooser to set the background and foreground colors. For that, see the aptly named "Color Choosers" Tech Tip for November 24, 1999 (http://java.sun.com/developer/TechTips/1999/tt1117.html#tip1). For a discussion of working with strokes, see the May 20, 2003 Tech Tip titled "Drawing Dashed Lines with Stroke" (http://java.sun.com/developer/JDCTechTips/2003/tt0520.html#1). The solution presented at the end of this tip only uses solid lines though. For the ambitious developer, feel free to add support for dynamic dash phases. For both the Stroke Size and Rotate menu items, you should use a JOptionPane to get the numerical input. Your program can have an input field that accepts input. Or the program can show a set of possible values and let the user pick one. Restricting input to a fixed set of input is usually less error prone. So if you decide to take the latter approach, here is how to show a set of possible values for the stroke size: Object[] possibleValues = { "1", "1.5", "2", "2.5", "3", "3.5", "4", "4.5", "5" }; Object selectedValue = JOptionPane.showInputDialog( canvas, res.getString("Choose_stroke_size"), res.getString("Stroke_Size"), JOptionPane.INFORMATION_MESSAGE, null, possibleValues, possibleValues[0]); if (selectedValue != null) { float size = Float.parseFloat((String)selectedValue); canvas.setStrokeSize(size); } Basically, the code above fills an array with the available choices and picks the initial default. The res variable represents a resource bundle for getting the strings to show. Note that until a user selects an area, choosing an operation under the Edit menu should not do anything. The Rotate action should use an AffineTransform. For information about AffineTransform, see the September 9, 2003 Tech Tip titled "Understanding AffineTransform" (http://java.sun.com/developer/JDCTechTips/2003/tt0909.html#2.) When rotating, be sure the program rotates from the center of the area, not from the origin of the image. Also, don't forget to convert the rotation angle to radians. For the flipping operations, you could use another transform or use one of the many drawImage methods of Graphics. For instance, here is code that does a horizontal flip. g.drawImage(buffer, selectedArea.x + selectedArea.width, selectedArea.y, selectedArea.x, selectedArea.y + selectedArea.height, selectedArea.x, selectedArea.y, selectedArea.x + selectedArea.width, selectedArea.y + selectedArea.height, this); Notice that the location arguments in the code above are in sets of four. If you look carefully, you'll see that the x coordinates are swapped in the two sets. This results in a horizontal flip. Swapping y coordinates would do the vertical flip. The final operation worth discussing is how to save the image. Use a JFileChooser for selecting where to save. See the June 15, 1999 Tech Tip on file choosers (http://java.sun.com/developer/TechTips/1999/tt0615.html#tip1) for more information on that topic. The October 21, 1999 Tech Tip, "Creating Image Thumbnails", (http://java.sun.com/developer/TechTips/1999/tt1021.html#tip1) discusses how to save images. However, a newer (and better) approach to saving images is to use the ImageIO class. For information on using this class see the formal documentation (http://java.sun.com/j2se/1.4.2/docs/api/javax/imageio/ImageIO.html) Look for a future Tech Tip that explores the ImageIO class. You can find one possible solution to the challenge at: http://java.sun.com/developer/JDCTechTips/2003/tts1223.txt. As mentioned earlier, the solution comprises three classes: one class presents the GUI, the second provides the drawing canvas, and the third is the resource bundle for the text strings. If you're looking for even more operations to support in the drawing program, consider the following: o Allow the user to add text. Let the user pick which font (style or size) to use. o Support cutting and pasting images to or from other applications. o Support dashed lines and shapes with borders. o Support fill patterns of shapes (that is, not only solid images). o Add an undo/redo queue. o Give the user the option of saving in different formats, including specifying a compression level for JPG. o If saving to PNG, allow the user to play with transparency. Feel free to add any more options that you find in your favorite drawing program. Solutions for these added options are left as an exercise to the reader. Search through the list of Core Java Technologies Tech Tips (http://java.sun.com/developer/JDCTechTips/index.html) to find even more features that you can add to the program. . . . . . . . . . . . . . . . . . . . . . . . IMPORTANT: Please read our Terms of Use, Privacy, and Licensing policies: http://www.sun.com/share/text/termsofuse.html http://www.sun.com/privacy/ http://developers.sun.com/dispatcher.jsp?uid=6910008 * FEEDBACK Comments? Please enter your feedback on the Tech Tips at: http://developers.sun.com/contact/feedback.jsp?category=sdn * SUBSCRIBE/UNSUBSCRIBE Subscribe to other Java developer Tech Tips: - Enterprise Java Technologies Tech Tips. Get tips on using enterprise Java technologies and APIs, such as those in the Java 2 Platform, Enterprise Edition (J2EE). - Wireless Developer Tech Tips. Get tips on using wireless Java technologies and APIs, such as those in the Java 2 Platform, Micro Edition (J2ME). To subscribe to these and other JDC publications: - Go to the JDC Newsletters and Publications page, (https://softwarereg.sun.com/registration/developer/en_US/subscriptions), choose the newsletters you want to subscribe to and click "Update". - To unsubscribe, go to the subscriptions page, (https://softwarereg.sun.com/registration/developer/en_US/subscriptions), uncheck the appropriate checkbox, and click "Update". - To use our one-click unsubscribe facility, see the link at the end of this email: - ARCHIVES You'll find the Core Java Technologies Tech Tips archives at: http://java.sun.com/developer/JDCTechTips/index.html - COPYRIGHT Copyright 2003 Sun Microsystems, Inc. All rights reserved. 4150 Network Circle, Santa Clara, California 95054 USA. This document is protected by copyright. For more information, see: http://java.sun.com/developer/copyright.html Core Java Technologies Tech Tips December 23, 2003 Trademark Information: http://www.sun.com/suntrademarks/ Java, J2SE, J2EE, J2ME, and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries.