package mvp; import java.awt.*; import java.awt.event.*; import java.util.*; import java.io.*; /**** * * Class View is an abstract parent class for view classes in an MVP design. * See * Fisher SE lecture notes for further discussion of the MVP design * methodology. *

* View implements Observer since it is often convenient for a view to observe * its companion model, particularly when a model has multiple views that all * need to change simultaneously when the model changes. View implements * Serializable as a convenience for serializing models that refer to views. * Typically, view data are not themselves worthy of serialization, but when a * model refers to a view, that view needs to be Serializable in principle in * order for serialization to proceed without problems. * * @author Gene Fisher (gfisher@calpoly.edu) * @version cvs 1.15, 2003/02/02 * */ public abstract class View implements Observer, Serializable { /** * Construct a view with the given Screen and Model. Initialize other data * members to null or false, as appropriate. The given screen is that in * which the view will insert itself to be physically displayed. The model * is the companion model in an MVP design. *

     * pre: screen != null;
     *
     * post: (this'.screen == screen) && (this'.model == model) && 
     *       (window' == null) && (shown' == false) &&
     *       (editable' == false) && (closeAdapter == false);
     *                                                                   
*/ public View(Screen screen, Model model) { this.screen = screen; this.model = model; shown = false; editable = false; } /** * Construct this with a null screen and model. Initialize other data * members to null or false, as appropriate. *
     * post: (this'.screen == null) && (this'.model == null) && 
     *       (window' == null) && (shown' == false) &&
     *       (editable' == false) && (closeAdapter == false);
     *                                                                   
*/ public View() { shown = false; editable = false; } /** * Run this by entering the screen's event loop, which will in turn display * the window of this and any widgets within the window. In a * non-modal GUI, only the topmost view will be run. In a modal GUI, run * is specialized in View subclasses to contain a modal event loop that * locks out events from other view components until the event loop has * been completed, e.g., by the user selecting 'OK' or 'Cancel'. *
     * pre: ! screen.isRunning();
     *
     * post: screen'.isRunning() &&
     *       forall (Component c | c in screen.getComponents())
     *           c.isDisplayed();
     *                                                                   
* where isRunning is an assumed predicate of Screen that * returns true if the Screen's event-handling loop is running. */ public void run() { // A noop in Java } /** * Compose the interface components of this, setting the window and/or * widget fields to the top-level of the composition. Note that compose * does not physically display this, it only constructs its components so * it may subsequently be displayed with the run and show methods. As a * convenience to callers, compose returns the widget field of this, since * it is frequently accessed after composition. *
     * post: return == widget;  // Compose must be specialized in subclasses.
     *                                                                   
*/ public Component compose() { return widget; } /** * Insert the window of this into the screen, thereby physically displaying * it. Upon insertion, the window will be the frontmost of all other * screen windows. If this is already displayed, Show has the effect of * moving its window to the front of all other windows. *

* To move the window to some position other than the front, extract the * window with this.getWindow and manipulate it with methods available in * the Window class. *

* The screen position of the window will be selected by the underlying * window manager. To show the window of this at a specific screen * position, use the overloaded version of Show specified below. *

     * pre: ;
     *         
     * post: if (window != null)
     *       then screen'.isDisplayed(window) &&
     *            screen'.IsFrontmost(window) &&
     *            shown' == true;
     *       else System.out.println("error");
     *                                                                   
*/ public void show() { if (window == null) { System.out.print("The window data member of this view:\n "); System.out.print(this.getClass().getName()); System.out.println("\nis null."); } else { window.setVisible(true); shown = true; } } /** * Same specs as Show(), except the window is shown at the given x,y * coordinate. The x,y coordinate system is in units of screen pixels, * with the 0,0 origin in the lower left corner of the screen. The given * x,y coordinates refer to the lower left corder of the window. */ public void show(int x, int y) { window.setLocation(new Point(x, y)); show(); } /** * Remove the window of this from the screen, thereby physically * undisplaying it. *
     * pre: ; // Note that the window may or may not be currently displayed
     *         
     * post: ! screen'.isDisplayed(window) &&
     *       (shown' == false);
     *                                                                   
*/ public void hide() { window.setVisible(false); shown = false; } /** * Update the displayed data in this. The processing involved in this * update is strictly view specific. Generally, the view will call one or * more methods in the companion model to obtain the necessary data to * perform the update. *

* Views that do not change in response to changes in model data typically * do not implement update. For example, a dialog that is used strictly * for user input does not need to implement update. In contrast, a view * that displays changing model data does indeed need to implement update. *

     * post: ;   // Update must be specialized in subclasses.
     *                                                                   
*/ public void update(Observable o, Object arg) {} /** * Return the model of this. * * post: return == model; */ public Model getModel() { return model; } /** * Set the model of this to the given model, if the model is not already * set. This setModel method is used if this must be constructed before * its companion model is constructed, and therefore the model will not be * available to pass to the constructor. *
     * pre: this.model == null;
     *
     * post: this.model' == model;
     *                                                                   
*/ public void setModel(Model model) { if (this.model == null); this.model = model; } /** * Return the window of this. *
     * post: return == window;
     *                                                                   
*/ public Window getWindow() { return window; } /** * Return the widget of this. *
     * post: return == widget;
     *                                                                   
*/ public Component getWidget() { return widget; } /** * Return true if this is currently displayed on the screen. *
     * post: return == shown;
     *                                                                   
*/ public boolean isShown() { return shown; } /** * Return true if this is currently editable. The editability of a view * dictates whether the user is allowed to enter values in the view's * components that are normally editable, e.g., string or text editors. *
     * post: return == editable;
     *                                                                   
*/ public boolean isEditable() { return editable; } /** * Set the editability of this to the given boolean value. *
     * post: this'.editable == editable;
     *                                                                   
*/ public void setEditable(boolean editable) { this.editable = editable; } /** * Perform the necessary set up to call or not to call the companion * model's exit method when the user closes this' window. When * exitOnClose is true, model.exit is called upon window * close; when exitOnClose is false, model.exit is not * called. From the user's perspective, the close is performed using a * command provided by the underlying window manager, e.g., a window close * button. *
     * pre: (window != null)
     *
     * post: if (exitOnClose == true)
     *       then (exists (WindowAdapter wa)
     *               (wa in window.getListeners(WindowAdapter.class)) &&
     *               (wa.getClass().getDeclaredMethod(
     *                 "windowClosing", {WindowEvent.class})).invokes(
     *                   model.getClass().getDeclaredMethod("exit", null)) &&
     *               (closeAdapter' == wa))
     *       else !(exists (WindowAdapter wa)
     *               (wa in window.getListeners(WindowAdapter.class)) &&
     *               (wa.getClass().getDeclaredMethod(
     *                 "windowClosing", {WindowEvent.class})).invokes(
     *                   model.getClass().getDeclaredMethod("exit", null))) &&
     *               (closeAdapter' == null)
     *                                                                   
*/ public void setExitOnClose(boolean exitOnClose) { /* * Outta here if window is null. */ if (window == null) return; /* * If the exitOnClose input argument is true, add a listen-for-close * window adapter to this' window, if one hasn't already been added. * If false, remove the listener if it's there. */ if (exitOnClose && (closeAdapter == null)) { window.addWindowListener(closeAdapter = new WindowAdapter() { public void windowClosing(WindowEvent e) { model.exit(); } }); } if (!exitOnClose && (closeAdapter != null)) { window.removeWindowListener(closeAdapter); closeAdapter = null; } } /** The unique companion model for this */ protected Model model; /** The physical display screen for this. Only views that have a non-null * Window (see below) need a value for the Screen. Otherwise, the value of * the Screen member is null. */ protected Screen screen; /** The physical UI window for this. The Window is the stand-alone * top-level window for the view, displayed on the screen by the underlying * window manager. For views that do not have a managed window, e.g., * pulldown menu or dialog contained in another view, the Window member is * null. */ protected Window window; /** The outermost interactive element (i.e., "widget") for this. In some * toolkits, Window and Widget are the same type. In such cases, if a view * has an non-null Window, then it does not need a value for Widget. In * toolkits such as Java Swing, where Window and Widget are distinct types, * a view may need both a value for the Window and Widget. */ protected Component widget; /** True if the window is displayed */ protected boolean shown; /** True if this is editable */ protected boolean editable; /** The exit-on-close listener */ protected WindowAdapter closeAdapter; }