/* FlowableExample class for J2PrinterWorks
 * Outline of a custom J2PrinterWorks paginating component.
 *
 * FlowableExample is a J2Pageable subclass.  It implements Flowable, the
 * interface used to implement objects which are able to print incrementally,
 * advancing by one rectangular area at a time, see Flowable interface
 *
 * (C) Copyright 2009, Wildcrest Associates (http://www.wildcrest.com)
 *
 * This source code may be freely used, modified, incorporated, and
 * distributed without restriction as part of any software that uses
 * J2PrinterWorks by Wildcrest Associates.
 */

import javax.swing.*;
import java.awt.*;
import java.beans.*;


public class FlowableExample extends J2Pageable {

    private Object object=null; // your object to be printed by this instance

    private int cursorY;        // a value that remembers progress point when printing object

                                // this example assumes cursor is a Y coordinate advancing down the page,
                                // but could be just an index within the object or a Point remembering
                                // both X and Y if advancing by rectagular areas.

    private boolean firstTime=true;  // useful if special things to do the first time


    // constructors
    public FlowableExample() { }
    public FlowableExample(Object obj) { setObject(obj); }


    public void printFlowable(Graphics g, int availableWidth, int availableHeight, int nextFullPageWidth, int nextFullPageHeight) {
        // the availableHeight, availableWidth, etc. values are already adjusted according to scale factor

    // if desired: prepare object for printing
        //if (firstTime) {
              // maybe want to calculate and cache certain values the first time
              // or may need to add to a frame and pack in order to make printable, etc.
        //    firstTime = false;
        //}

    // if desired: shrink-to-fit so width fits to page
        // Dimension preferredSize = object.getPreferredSize();
        // int objectWidth = preferredSize.width;
        // int objectHeight = preferredSize.height;
        // if (objectWidth == 0 || objectHeight == 0) { // if nothing to print
        //     usedX = 0; usedY = 0; doneX = true; doneY = true; return;
        // }

        // double additionalScale = 1.0;
        // if (objectWidth>availableWidth) additionalScale = ((double)availableWidth)/objectWidth;
        // availableWidth = (int) (availableWidth / additionalScale);
        // availableHeight = (int) (availableHeight / additionalScale);


    // find page break (simple example showing vertical pagination only)
        // figure out how much more of "object" starting at "cursor" fits in availableWidth x availableHeight
        // if can't make progress in availableWidth x availableHeight, then set doneX=doneY=false, usedX=usedY=0, and return
        // which causes J2Pageable to skip to next full page
        // but then you must make progress in nextFullPageWidth x nextFullPageHeight, otherwise this is an error
        // nextFullPageWidth and Height available for determining if fit is better in remaining space or next full page

        int ystart = cursorY;
        int yend = ystart + availableHeight; // add available printing space

        // to do: figure out where short of yend to break (or end) object and set yend to this smaller value

        if (yend == ystart) { // if can't make progress...
           if (availableHeight == nextFullPageHeight) { // if available height is a full page...
              yend = ystart + availableHeight;          // then force progress here (this breaks object on pixel boundary)
           }
           else {                                       // else skip to next page
              doneX = doneY = false; usedX = usedY = 0; return;
           }
        }

        int ylen = yend - ystart;

    // if desired: adjust for alignment of object on page
        // double xLoc = 0.0;          // initialize to LEFT alignment
        // if (horizontalAlignment==CENTER) xLoc = (availableWidth - objectWidth)/2.0;
        // else if (horizontalAlignment==RIGHT) xLoc = availableWidth - objectWidth;

        // double yLoc = 0.0;          // initialize to TOP alignment
        // if (verticalAlignment==CENTER) yLoc = (availableHeight - ylen)/2.0;
        // else if (verticalAlignment==BOTTOM) yLoc = availableHeight - ylen;


    // draw next section of object (only draw if g isn't null)
        if (g!=null) {
            Graphics2D g2d = (Graphics2D) g;
            java.awt.geom.AffineTransform startingTransform = g2d.getTransform();

            // if desired: g2d.scale(additionalScale, additionalScale);
            // if desired: g2d.translate(xLoc,yLoc); // starts pre-translated to upper left corner of page body

            g2d.translate(0, -ystart); // translate next section of object to position on page body
            g2d.setClip(0,ystart,objectWidth,ylen); // clip to next section of object to print

            // to do: draw your object = g2d.drawLine(...), g2d.drawString(...), object.print(g2d), etc.

            g2d.setTransform(startingTransform);     // restore translation position
        }


    // done, calculate return values
        usedX = availableWidth;     // simple case if no horizontal pagination

        usedY = ylen;
        // if desired: if (verticalAlignment!=TOP) usedY = availableHeight; // CENTER & BOTTOM use all available space on this page

        // if needed: usedX = (int)(usedX * additionalScale);               // factor in possible additionalScale
        // if needed: usedY = (int)(usedY * additionalScale);

        doneX = true;               // simple case if no horizontal pagination
        doneY = yend>=objectHeight; // as needed: use this or some other test for whether we're done printing object

        cursorY = yend;             // advance cursor to next section of object to print on next call
    }


// other Flowable methods
    private int usedX=0;
    private int usedY=0;
    public int usedX() { return usedX; }
    public int usedY() { return usedY; }

    private boolean doneX=false;
    private boolean doneY=false;
    public boolean doneX() { return doneX; }
    public boolean doneY() { return doneY; }


    public void resetFlowable() { // method called to restart printing of object from beginning
        cursorY = 0;
        doneX = doneY = false;
        usedX = usedY = 0;
        // if desired: firstTime = true;
    }


// Properties

//--object-------------------------------------------------------------------------------------------------
    /** Specifies object to be printed.
      * @param object to be printed */
    public void setObject(Object obj) {
        this.object = obj;
        firstTime = true; fire("object");
        }
    /** Returns the object to be printed. */
    public synchronized Object getObject() { return this.object; }

//-----------------------------------------------------------------------------------------------------------
    /** Add a PropertyChangeListener for this component */
    public void addPropertyChangeListener(PropertyChangeListener l) { changes.addPropertyChangeListener(l); }
    /** Remove a PropertyChangeListener for this component */
    public void removePropertyChangeListener(PropertyChangeListener l) { changes.removePropertyChangeListener(l); }
    private PropertyChangeSupport changes = new PropertyChangeSupport(this);
    private void fire(String propertyName) { changes.firePropertyChange(propertyName, null, null); }
//-----------------------------------------------------------------------------------------------------------

}
