Assignment 2: Extending Swing components
This is an INDIVIDUAL assignment.
Due: Week 6, February 11.
Objective
In the last assignment you learned how to put together Swing applications by using existing components. In this assignment, you'll learn how to create your own Swing component from scratch and populate your journal with notes, images and sketches. You'll be building the tools that let you make multiimedia journal entries, including "energy use views" of the journal application. Entries can contain simple text as well as an imported image as well as sketches and annotations added by the user. This means that you are developing several custom components for content:
- a JournalEntryPanel. The JournalEntry panel will contain a content area that can hold text and/or graphics/images. It contains
- an ImagePanel. The image panel is where you import an image and draw or write on it. You can also use this panel simply to sketch an image to add to the journal.
- the EnergyUse View. This will be a static graphic chart for now. You can also add annotations to the EnergyUseView.
- a JournalView: the collected view of journal entries. For the moment this can be a simple list.
When you have finished this assignment you will have gained:
- Experience with the Swing drawing pipeline.
- experience with Swing layout managers and custom component architecture
- Experience writing a variety of input listeners.
Description
This seems like a big assignment, but it is extremely modular, and you can do it in pieces. Don't be intimidated by the length of the homework description! Although long, most of this writeup is here just to provide detail about what I expect from this assignment, as well as some hints about how best to do the implementation.
In this assignment, you will create custom Swing components that serve as the content editors and viewers in the energy Journal application. The basic idea is that the journal contains entries that each can comprise text/notes, optional images imported from disk, and optional sketches and notes entered freehand by the user. The Journal Entry panel or the JournalView will sit in the blank content area of the shell you built in Assignment 1 (you toggle between them). The EnergyUse view will be a separate window. These are described in detail below.
There are a number of specific requirements for this assignment:
- Journal entry.
- You use the JournalEntry panel to (surprise) add information to your journal. It should contain a scrolling text input area that collects text and expands wih a scrollbar as you fill the field and a blank image area (the ImagePanel). A Journal Entry menu should be enabled that allows you to "import picture" and "add annotations". It has a title/subject area (so that you can add mutliple entries on the same day). It is indexed by date. NOTE: I am not expecting you to save and retrieve journal entries from an external file ... YET.
- ImagePanel
- The ImagePanel is where you work with pictures. You can import a picture, add annotations to it or sketch on the "back". I will cover both in order here,
- Picture:Your component will hold a single image that you import. Since the component may be larger or smaller than the image it displays (because the user may resize the window, for example), you need to provide a background behind the image, and a "frame" or some sorts around it. For example, you could do a beige paper background, or something with a pattern (blue graph paper maybe). For the frame around the picture you can have a simple white border or something resembling a film slide, or "scrapbook corners". The background should be rendered underneath the image itself. The frame can either be rendered above or below the image.
- Hint: For background graphics, you can simply write a little routine that draws the necessary fills and lines, and call it first thing in your UI class's paint routine. This will ensure it gets drawn before any stuff that you render later in that route.
- Flipping to annotate/edit
- With physical paper images, people often flip them over to write notes and other annotations on the back. Your ImagePanel should support this also. Double-clicking on the image or selecting "add annotations" should cause it to be replaced by a plain white surface for annotation. All of the rest of your component (the background and frame) should be drawn the same--only the image should be replaced. When in this mode, you can annotate the photo's "back" via drawn strokes and typed text (see below). Double-click on the image back again to flip it over and see the image again. The current "flip state" of a given image should be retained, meaning that if you come back to thisImagePanel again it should be in the same flipped or unflipped state that you left it in. Note that you do not have to do any fancy animation for flipping, although you'll get some extra credit if you do.
- Support for drawn strokes.
When in the flipped state, you should be able to draw onto the image back using the mouse (if you're using a pen tablet, the pen also produces mouse events and so this sort of implementation will also work nicely on a pen-based computer). What this means is that the user should be able to draw freehand strokes by dragging the mouse on the image back with the button pressed. The component should show the stroke while it is in the process of being drawn, to give appropriate feedback to the user.
- Hint: Remember that you'll need to redraw all of these same strokes anytime the Swing repaint pipeline tells you that you need to paint your component. So, the classic way to do this is to add strokes to a display list that contains the things to be rendered, and then in your paint code you simply iterate through the items to be painted, rendering them to the screen.
- Hint: Painted strokes will look much better if you use Java2D's anti-aliasing mechanism. Look at the setRenderingHints() method on Graphics2D.
- Support for typed text. When in the flipped state you should also allow typed text to be entered onto the image back. The way this should work is that the user clicks on the image back to set an insertion point for the text. Then, any typing will begin to fill the image back starting at that insertion point. Clicking again will reset the insertion point to another position. While you don't have to do any especially fancy text processing (no ligatures or custom fonts or anything like that), you should make the basics work correctly. The basics are:
- You should implement word wrap. This means that when your typing hits the end of the line you should find the rightmost whitespace in the line, break the line there, and put the remaining word on a new line. If there is no whitespace in the line then you can just break the line at the last character that will fit on the line.
- You should reflow the text if the content area is resized. This means re-word wrapping the lines so that they fill the new size appropriately.
- You do not have to implement more complicated features (although you're welcome to for extra credit). For example, you do not have to implement:
- Backspacing over already entered characters.
- The ability to put the insertion point into already-entered text and edit it.
- The ability to select a span of text.
- Hint: While all the word wrapping and reflowing stuff may seem difficult, it's possible to architect the code in such a way that you kill lots of birds with one stone. The key is to remember that, like with stokes above, you'll need to keep a data structure containing the text that will be rendered by your component. So one way to architect things is to simply create a new object to hold a text block whenever the insertion point is set; this object only needs to remember the insertion point and the set of characters entered at that point. Whenever characters are typed they are simply added to the current text block object. The job of your paint code, then, is simply to iterate over the list of text blocks and draw them to the screen, wrapping as you draw based on the current size of the component. This strategy will take care of word wrap and refilling via one implementation in the component's painting code.
- Hint: Telling the difference between "pen mode" and "draw mode" should be easy: If you see a mouse down followed by movement, you can assume you're drawing. If you see a mouse click, you can assume you've set the text insertion point for keyboard entry.
- EnergyView
- Design and draw a simple line graph in your EnergyView. Make one axis time and the other kWH (amount of energy consumed) . Don't make it animate for now, but do define a data strutcture and class that you will be able to use in later assignments. In the energy view. the user should be able to select a point on the line that corresponds to a time and add an annotation. double clicking the point should open a text input area with the time marked where the user can enter notes. This means you need to add several fields to your EnergyView window.
- JournalView. You should provide a navigatable view of journal entries in your sidebar. A simple scrolling lit will suffice for now, but implementing the calendar thmbnail view with selectable date will gain you extra points (Hint: you would use a JTable). For the moment, as you are only expected to to provide one journal entry. this list may be small.
- Data structures: IT should be clear that you will need to index some of the user's input and tools - for example, you would index notes in the EnergyUse view by the time selected. We will go over some useful data structures for doing this in the workshops.
- Integration with the rest of the application. Once you've implemented and debugged yourImagePanel, it's time to integrate it into the application you wrote for Assignment #1. When your application starts up, it should contain no images, and hence no ImagePanels.
- Selecting Import image should prompt the user for a image file (via a file dialog), and create a newImagePanel containing this image and display it in the content area.
- The currently displayedImagePanel should be used in the central content area of your application, should resize properly when the window is resized, etc. (You don't have to worry about scrolling within aImagePanel, although you can implement this if you want to. You also don't have to worry about doing fancy stuff like scaling images.)
- Selecting Delete image should delete the currentImagePanel. You'll probably want to update your application to have a list of yourImagePanels so that you can keep track of them.
- You must have at least oneImagePanel - but you can get extra credit by providing multiples, as
- Selecting Import images from Folder should prompt the user for a directory (via a file dialog) and create a set ofImagePanels containing the images.
- Selecting Previous and Next should move between theImagePanels (if you have multiples).
Deliverable
This is an INDIVIDUAL assignment; while you may ask others for help on Java or Swing details, please write the code on your own. While you can use any development environment you choose, you are not allowed to use "GUI builder" type tools (like JBuilder or Eclipse GUI Builder).
While this assignment is significantly more difficult than the previous one, most of the parts should be fairly straightforward. Start with the basic component architecture and get that working in your application first; one easy way to do this is to implement the UI class's paint method so that it only draws the component background. This way you'll be able to see your component show up and know it's working correctly. After this do the stroke processing (it's the next easiest), followed by the text processing (slightly more complicated). Finally, once all of this is working, it should be fairly straightforward to keep a list ofImagePanels in your application and make the New/Delete/Next/Previous controls work.
To turn in the assignment, please follow the same process as last time:
0. Make sure your program is runnable from the command line using the command "java energyJournal". Use exactly this name (with no package) to make things easier on me and the TAs.
1. Create a new directory using your last name as the name of the directory.
2. Compile your application and place both the sources and classfiles into this directory (they can be at the top-level or in a subdirectory, whatever you want).
3. Put a README.txt file (described below) into the top level of the directory. This file should contain your name and email address, the version of Java you used (1.5.x or 1.6.x, please) as well as any special info I might need in order to run your code (command line arguments, etc.) BE SURE TO TELL ME ANY SPECIAL EXTRAS YOU ADDED OR ISSUES YOU HAD.
5. VERY IMPORTANT. Submit a thorough Software Design Document (see the explanation here).
4. ZIP this directory and submit it via webct.